Sunday, December 21, 2014

The Value of Annotation @Temporal for Date Related Columns

The annotation @Temporal in official javadoc is described like below:
This annotation must be specified for persistent fields or properties of type java.util.Date and java.util.Calendar.

So when the field variable is a java.util.Date or java.util.Calendar, it MUST be annotated by @Temporal.

There are 3 values for @Temporal

  • TemporalType.DATE
  • TemporalType.TIME
  • TemporalType.TIMESTAMP

Here is a demo to show the usage of there 3 values. The Entity 'MyEntity' has a field called 'lastUpdateTime' which is a defined as a java.util.Date variable. let's update it in java like this:

  import java.util.Date;
//...
// MyEntity is the JPA entity, em is EntityManager context
MyEntity e = em.find(MyEntity.class, 1L); //get the row with id=1
e.setLastUpdateTime(new Date());

TemporalType.DATE


If the column annotated with @Temporal(TemporalType.DATE) like this:

  @Temporal(TemporalType.DATE)
@Column(name="LAST_UPDATE_TIME")
private Date lastUpdateTime;

The record in database after update will look like:Snap_2014.12.22 12.49.51_001


TemporalType.TIME


If the column annotatedwith @Temporal(TemporalType.TIME) like this:

  @Temporal(TemporalType.TIME)
@Column(name="LAST_UPDATE_TIME")
private Date lastUpdateTime;

The record in database after update will look like:Snap_2014.12.22 13.06.58_002


TemporalType.TIMESTAMP


If the column annotated with @Temporal(TemporalType.TIMESTAMP) like this:

  @Temporal(TemporalType.TIMESTAMP)
@Column(name="LAST_UPDATE_TIME")
private Date lastUpdateTime;

The record in database after update will look like:Snap_2014.12.22 13.10.14_003

Thursday, December 18, 2014

How to configure primary key's generator in JPA entity for oracle

In JPA there are 4 strategies to automatically generate value for a primary key column:

  • Auto
  • Identity
  • Sequence
  • Table

To some extent, there are only 3 strategies, the last 3 ones. Because the first 'Auto' just let the JPA implementation to choose which one of the rest 3 strategies it will use. For example working with Oracle + EclipseLink 2.5.0,  the "Auto" will result in the strategy "Table" being choosen . This behavior depends on the database and the JPA implementation in your project.

0. Before we start

In this tutorial, Oracle is database and EclipseLink is JPA implementation.  The table name in this demo is T_JUST_FOR_TEST, which only have 1 field 'id' as its primary key for simplicity's sake.

In JPA the annotation for value generation of  primary key is @GeneratedValue

Note : The value generation strategy only  take effect when you try to write back to database from your java application. In other words, if the table is read-only in the Java application,  Any specified strategy is meaningless in Entity class.

1. Use Strategy Identity

Oracle don't support Identity.

2. Use Strategy Sequence

2. 1 Define Sequence in Database

Make sure there is sequence that you can use or you can define a new one like this.

-- Create sequence 
create sequence SEQ_JUST_FOR_TEST
minvalue 1
maxvalue 9999999999999999999999999999
start with 1
increment by 1;

The created sequence is named 'SEQ_JUST_FOR_TEST'. it starts from 1 and increase 1 for every next value.


2.2 Define Entity class


Use Annotations for primary key column. First define a sequence generator, then use the generator.

package com.shengwang.example;

import java.io.Serializable;
import javax.persistence.*;

@Entity
@Table(name="T_JUST_FOR_TEST")
public class TJustForTest implements Serializable {
private static final long serialVersionUID = 1L;

@Id
@SequenceGenerator(name="SEQ_GEN", sequenceName="SEQ_JUST_FOR_TEST", allocationSize=1)
@GeneratedValue(strategy=GenerationType.SEQUENCE, generator="SEQ_GEN")
private long id;

public TJustForTest() {}

public long getId() {
return this.id;
}

public void setId(long id) {
this.id = id;
}
}

In the above Entity definition class, The annotation @SequenceGenerator is used to define a Sequence Generator named "SEQ_GEN", which is then used in the @GeneratedValue annotation.  The attribute sequenceName is the real sequence  name in Oracle. The allocationSize is the increment for every step.  Make sure the value of allocationSize matches the corresponding value in the sequence definition in Oracle.  In the annotation @GeneratedValue, strategy "GenerationType.SEQUENCE" is specified, and generator set to the sequence generator "SEQ_GEN" defined one line above.


Every time when insert a new TJustForTest entity record into the table like below, the primary key id will increase one.

  // em is the injected EntityManager;
TJustForTest t = new TJustForTest();
em.persist(t);

3. Use Strategy Table


3.1 Define table in database


A table need to be defined in database.

create table T_GENERATOR1
(
gen_key VARCHAR2(255) not null,
gen_value NUMBER,
constraint pk_gen_key PRIMARY KEY (gen_key)
)

The table works somehow like a key-value pair or hash table. Every row of the table can  logically be treated as a sequence.  Normally, the data type of key column is string and data type of value column is number. To make it more readable, A string combines schema(MONITOR2012) and table name(T_JUST_FOR_TEST) is used as key in this tutorial.


3.2 Insert one row into the table

insert into T_GENERATOR (Gen_Key,GEN_VALUE) values ('MONITOR2012.T_JUST_FOR_TEST',0)

This one row data has to be insert in to the table T_GENERATOR before we can use it as generator in JPA entity class.


3.3 Define Entity Class


Use Annotations for primary key column. First define a table generator, then use the generator.

package com.shengwang.example;

import java.io.Serializable;
import javax.persistence.*;

@Entity
@Table(name = "T_JUST_FOR_TEST")
public class TJustForTest implements Serializable {
private static final long serialVersionUID = 1L;

@Id
@TableGenerator(name="TABLE_GEN",table="T_GENERATOR", pkColumnName = "GEN_KEY", pkColumnValue = "MONITOR2012.T_JUST_FOR_TEST", valueColumnName = "GEN_VALUE", initialValue = 1, allocationSize = 1 )
@GeneratedValue(strategy = GenerationType.TABLE, generator="TABLE_GEN")
private long id;

public TJustForTest() {
}

public long getId() {
return this.id;
}

public void setId(long id) {
this.id = id;
}
}

The annotation @TableGenerator is used to define a table generator named "TABLE_GEN". This table generator is used as the generator for @GeneratedValue.


Let's go through all the rest attributes in annotation @TableGenerator in details.


table is the table name in database used. pkColumnName is the column name of the table.. pkColumnValue is the value of the key. These 2 attributes are logically combined as a where phase in SQL, " Select ... from T_GENERATOR where GEN_KEY='MONITOR2012.T_JUST_FOR_TEST'", to locate the single record in the database table. valueColumnName is the column name to store real id value. initialValue is the first value of the generator. allocationSize is the step to increase. 


This is how these attributes work in logic: Every time JPA implementation need to generate value, it will first find the table table, then find the row using pkColumnName and pkColumnValue, then read the value of valueColumnName, add allocationSize, use it and update the value back to valueColumnName.


Every time when insert a new TJustForTest entity record into the table like below, the primary key id will increase one.

  // em is the injected EntityManager;
TJustForTest t = new TJustForTest();
em.persist(t);

4. Summary

Strategy sequence and strategy table can be used in JPA entity class if working with Oracle. The strategy sequence is oracle dependent, it does not work if you change to other database like MySQL. The Strategy table is database independent, but seems a little bit more complex than sequence strategy.

Tuesday, December 9, 2014

How to create Java classes from existing tables in database for JPA

If JPA is your persistence layer in your project and you have tables already defined in database. You can use JPA tools in Eclipse to automatically create corresponding Java Entity classes from those tables in database.

In this article, Java JPA entities will be created from an oracle database on LAN.

1. what you need

  • Eclipse 4.3 +  ( In this article, Eclipse Java EE version 4.3.2 kepler is used )
  • Database connection configuration in Eclipse. In eclipse, configure a database connection to the oracle database.  If you don't know how to do that, read this article first How to add oracle DB connection in Eclipse.

2. Make sure you have JPA support enabled in your projects

Righ click on the project name and select "Properties"

Snap_2014.12.09 14.30.34_003

Use project facets if project facets not enabled

Snap_2014.12.09 10.02.48_001

Click the "JPA" if it's not selected, then click "OK" to close the project properties window.

Snap_2014.12.09 14.34.20_004

3. create Java classes from tables

After enabling JPA in project properties, now right click you eclipse project name again, you should see a new context menu item "JPA tools" appears. Choose "Generate Entities from tables"

If you see the "JPA tools" menu but the sub menu don't have "Generate Entities from tables"  item, make sure you right click on the project name instead of a package or a class file.

Snap_2014.12.09 14.49.33_006 

Select a database connection to let Eclipse get the tables  used to generated class from.  (See another tutorial on  how to configure a database connection in Eclipse)

Snap_2014.12.09 15.04.08_007

After connected to the database, schema and tables will be listed below, choose the tables you need in your Java project and click "Next"Snap_2014.12.09 15.07.21_008

In this tutorial  2 tables are selected. There is a  foreign key between 2 tables. Logically one  platform can have more than one devices. It's a "1 to many" mapping.

Next Eclipse automatically create associations between tables you select.  Click each association to see the details.

Snap_2014.12.09 15.20.46_009

In the final step, Eclipse offers the chance to change generated Java class name and the field name inside the generated java class.

Snap_2014.12.09 15.28.13_011

Snap_2014.12.09 15.29.02_012

Click "finish" and the Java classes will be created.  For example one of the Entity class created looks like:

Snap_2014.12.09 15.38.05_013

The created Java Entity class is well annotated and ready for use!

Monday, December 8, 2014

How to add oracle DB connection in Eclipse

Have a DB connection in Eclipse will be very helpful if you need use persistence frameworks like Hibernate or JPA.  It's normally a prerequisite for other functions, for example let eclipse create java entities class from database tables automatically.  Here's how to configure oracle connection in Eclipse.

1. what you need

  • Oracle JDBC driver. ( A jar file may look like ojdbcxxx.jar, if you don't have it on your local file system, download it from oracle website)

2. open "Data Source Explorer" view in Eclipse

From menu, select "window"->"Show View"->"Other"

Snap_2014.12.09 11.23.04_014

Next, Choose "Data Source Explorer" from category "Data Management"

Snap_2014.12.09 10.04.24_002

Then you should have the view opened.

3. Add new connection

Right click on the "Database Connection" in the "Data Source Explorer" view we just opened.  choose "New..."

Snap_2014.12.09 10.06.49_003 

Choose database type ( here is Oracle), give a name for this connection (here is "oracle")

Snap_2014.12.09 10.08.52_005

Next, select the oracle driver for this connection by click the follow icon pointed by the arrow.

Snap_2014.12.09 10.13.26_006

Next, choose oracle version (I'm using oracle 11 for this demo, you can pick up your version) ,  and click the "Jar List" tab to set the driver file path

Snap_2014.12.09 10.14.51_007

In the "Jar List" tab, replace the default non-exists oracle driver file, which is ojdbc14.jar, with the real one  in you local file system, which is d:\ojdbc6.jar

Snap_2014.12.09 10.17.27_009

Snap_2014.12.09 10.19.15_010 

Close the above window,  next provide connection information.

Snap_2014.12.09 10.22.01_012

Use "Test Connection" button to make sure every thing is OK, then Finish.

4. After Connection created

You can see the new oracle DB connection appears in the "Data Source Explorer" view.

Snap_2014.12.09 11.50.04_015

You can browse the database in the view within Eclipse.

Tuesday, December 2, 2014

How to disable c3p0 from writing log to screen (stderr)

Even if you have slf4j and logback configured in your project, the c3p0 insists on writing log to system error channel like this:

Snap_2014.12.03 11.17.45_001

The Eclipse console show log from c3p0 in red, because the texts are written to stderr.  The way to dismiss those red part from c3p0 is add two lines at the beginning of your code.

public static void main(String[] args) {
System.setProperty("com.mchange.v2.log.FallbackMLog.DEFAULT_CUTOFF_LEVEL", "WARNING");
System.setProperty("com.mchange.v2.log.MLog", "com.mchange.v2.log.FallbackMLog");

// rest of your code
}


Here is the final result looks like. the redundant c3p0 log to stderr is gone.



image

Monday, December 1, 2014

How to inject a logger variable by using annotation in Spring

In development, we need logger almost everywhere. you can inject a bean with annotation like inject other beans
package com.shengwangi.demo;
@Component
public class MyClass {
  @Loggable
  Logger logger;
  public void myMethod() {
    // do some thing
    logger.info("use the logger as usual"); 
  }
}
As you can see, annotation @Loggable is used to inject the logger. @Loggable is an annotation that we defined ourselves. Here is how to do it. (It's not my original idea, borrowed from stackoverflow Q&A here by Redder)

1. Define the annotation

Define the loggable annotation.
package com.shengwangi.demo;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

@Retention(RUNTIME)
@Target(FIELD)
@Documented
public @interface Loggable {}
Besides the package and import lines there are 3 annotations before the one-line definition of Loggable. @Retention(RUNTIME) means the Annotation will be retained by the compiler and can be used by VM at run time. @Target(FIELD) means it will be used to annotate fields of class. @Documented it will be documented when you run javadoc to create documents.

2. Define LoggableInjector Class

Let's see the code first.
package com.shengwangi.demo;

import java.lang.reflect.Field;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Service;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.ReflectionUtils.FieldCallback;

@Service
public class LoggableInjector implements BeanPostProcessor {

  @Override
  public Object postProcessAfterInitialization(Object bean, String beanName)
      throws BeansException {
    return bean;
  }

  @Override
  public Object postProcessBeforeInitialization(final Object bean,
      String beanName) throws BeansException {
    ReflectionUtils.doWithFields(bean.getClass(), new FieldCallback() {
      public void doWith(Field field) throws IllegalArgumentException,
          IllegalAccessException {
        // make the field accessible if defined private
        ReflectionUtils.makeAccessible(field);
        if (field.getAnnotation(Loggable.class) != null) {
          Logger log = LoggerFactory.getLogger(bean.getClass());
          field.set(bean, log);
        }
      }
    });
    return bean;
  }
}
The LoggableInjector implements Spring's BeanPostProcessor interface, which give you the chance to do something just before/after creation of beans. What the LoggableInjector class actually do is after creation of a bean object, find the field annotated by @Loggable, which we defined in step 1, then set the field value to a real logger.

3. Use annotation @Loggable to inject logger

Like we already did in the beginning of this article, using the annotation to inject logger to your beans.
@Loggable Logger logger
One thing need to notice is that the real logger is created after the bean object has been initialized, which means you can NOT use logger in the constructor of the bean. Because at that time, the logger field has not be defined, you'll get NullPointerException Error.
Powered by Blogger.

About The Author

My Photo
Has been a senior software developer, project manager for 10+ years. Dedicate himself to Alcatel-Lucent and China Telecom for delivering software solutions.

Pages

Unordered List