JPA Annotations – @GeneratedValue, @SequenceGenerator and @TableGenerator

We’ve already discussed a different kind of primary keys, either simple or composite primary keys have assigned an inline values in the executable applications that were made. by looking into @Id, @IdClass or @EmbeddedId examples you could see that. But what if we would have some generator to generate these primary values. This tutorial explains the ID generator concept in details. The Java Persistence API including the EclipseLink distribution provides several types of primary key generation annotations.

@GeneratedValue

The @GeneratedValue consider the entry point for primary key generation, it provides the specification of generation strategies for the values of primary keys. The GeneratedValue annotation may be applied to a primary key property of field of an entity or mapped superclass  in a conjunction with the Id annotation. The values that can be used with the @GeneratedValue are those values defined inside the enum GenerationType. GenerationType.java

public enum GenerationType {TABLE,SEQUENCE,IDENTITY,AUTO};
  • TABLE: Is a generator type value indicates that the must assign primary keys for the entity using a Database Table to ensure uniqueness.
  • SEQUENCE & IDENTITY: Are a generator types that specify the use of a database sequence or identity column respectively.
  • AUTO: Is a generator type indicates that the persistence provider should select an appropriate strategy for the particular database.

Anatomy of @GeneratedValue

  • Target: Field and Method
  • Uses:@GeneratedValue
  • Argument:
    1. strategy(Optional): The primary key generation strategy that the persistence provider must use to generate the annotated entity primary key.
    2. generator (Optional): The name of the primary generator to use as specified in the SequenceGenerator or TableGenerator.

Let’s see the implementation of different strategies using the same entity. As you’ve noted before in the EclipseLink Tutorial, we’ve created a mapped superclass previously, so this tutorial should clarify all strategies using License class example.

License Entity

The following class shows you the License entity without using the @GeneratedValue.


package net.javabeat.eclipselink.data;

import javax.persistence.CascadeType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.MappedSuperclass;

@MappedSuperclass
public abstract class License {

@Id
 protected int licenseId; // This primary key field has no generator

@ManyToOne(cascade = CascadeType.ALL)
 @JoinColumn(name = "employeeId")
 private Employee employee;

public Employee getEmployee() {
 return employee;
 }

public void setEmployee(Employee employee) {
 this.employee = employee;
 }

public int getLicenseId() {
 return licenseId;
 }

public void setLicenseId(int licenseId) {
 this.licenseId = licenseId;
 }
}

  • The License mapped superclass provide an identity named licenseId.
  • The licenseId has no primary key generator
  • In case we’ve not used a generator and not provided a primary key value, the JPA will consider the default value for int primitive types (the default value is zero) and the persist will success for the first time. But when it comes into the second persist the JPA will throw an exception indicate that (Duplicate entry ‘0’ for key primary key).

Primary Key Generation Using Sequence Generator

At this example, you would be seeing a database sequence for generating a primary keys.

Anatomy of @SequenceGenerator

The @SequenceGenerator annotation defines a primary key generator that may be referenced by name when a generator element is specified for the GeneratedValue annotation.A sequence generator may be specified on the entity class or on the primary key field or property.

  • Target: Type, Method and Field
  • Uses:@SequenceGenerator
  • Argument:
    1. name (Required): A unique generator name that can be referenced by one or more classes to be the generator for primary key values.
    2. sequenceName (Optional): The name of the database sequence object from which to obtain primary key values.
    3. initialValue (Optional): The value from which the sequence object is to start generating.
    4. allocationSize (Optional): The amount to increment by when allocating sequence numbers from the sequence.

Database Sequence Creation

This sample of sequence creation should use the Oracle database, cause MySQL support auto increment which is not consider as a sequence. Figure 1.0 and 1.1 shows you the DriverLicense Table and sequence that created on the Oracle database. Note that the DriverLicense Table doesn’t consider any further relations as you’ve seen in the previous examples. Driver License Table

Figure 1.0

Driver License Sequence

Figure 1.1

License Entity Uses a Sequence Generator

The following License entity does use the sequence generator for generating the primary keys for the entities that created from the License (in case the license isn’t mapped superclass) and from any entity belongs to the License inheritance. License.java


package net.javabeat.eclipselink.data;

import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import javax.persistence.SequenceGenerator;

@MappedSuperclass
public abstract class License {

@Id
 @GeneratedValue(strategy=GenerationType.SEQUENCE,generator="LICENSE_SEQ")
 @SequenceGenerator(name="LICENSE_SEQ",sequenceName="LICENSE_SEQ",allocationSize=1)
 protected int licenseId;

public int getLicenseId() {
 return licenseId;
 }

public void setLicenseId(int licenseId) {
 this.licenseId = licenseId;
 }
}

DriverLicense.java


package net.javabeat.eclipselink.data;

import java.util.Date;

import javax.persistence.Entity;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

@Entity
@Table(name="driverlicenses")
public class DriverLicense extends License{

 private String driverLicenseName;
 @Temporal(TemporalType.DATE)
 private Date driverLicenseExpiryDate;
 @Temporal(TemporalType.DATE)
 private Date driverLicenseIssueDate;

 public String getDriverLicenseName() {
 return driverLicenseName;
 }
 public void setDriverLicenseName(String driverLicenseName) {
 this.driverLicenseName = driverLicenseName;
 }
 public Date getDriverLicenseExpiryDate() {
 return driverLicenseExpiryDate;
 }
 public void setDriverLicenseExpiryDate(Date driverLicenseExpiryDate) {
 this.driverLicenseExpiryDate = driverLicenseExpiryDate;
 }
 public Date getDriverLicenseIssueDate() {
 return driverLicenseIssueDate;
 }
 public void setDriverLicenseIssueDate(Date driverLicenseIssueDate) {
 this.driverLicenseIssueDate = driverLicenseIssueDate;
 }
}

  • The License entity uses the @GeneratedValue for providing a both of generator type and name.
  • The generator type is SEQUENCE and the name is LICENSE_SEQ.
  • The License entity uses the @SequenceGenerator for providing the name of the sequence that’s would be consumed by the @GeneratedValue, the name of the database sequence and the allocation size.

Required Persistence Configuration

persistence.xml

<persistence-unit name="EclipseLink-JPA-OracleDS" transaction-type="RESOURCE_LOCAL">
 <!-- Entities Created Before -->
 <class>net.javabeat.eclipselink.data.License</class>
 <class>net.javabeat.eclipselink.data.DriverLicense</class>
 <properties>
 <property name="javax.persistence.jdbc.url" value="jdbc:oracle:thin:@localhost:1521:xe"/>
 <property name="javax.persistence.jdbc.user" value="ucm"/>
 <property name="javax.persistence.jdbc.driver" value="oracle.jdbc.OracleDriver"/>
 <property name="javax.persistence.jdbc.password" value="ucm"/>
 </properties>
 </persistence-unit>

Required Libraries

As you’ve noted in the persistence.xml above, the database that’s being used for achieving a sequence generator is an Oracle database, so that you’ve had installed a new library in your project classpath that’s JAR is called ojdbc14.jar. for installing a new library you have to follow the below steps:

  • Right click on the project that you would be adding the JAR in its classpath.
  • Select Java Build Path from the left pane.
  • From the Java Build Path Area click on Add External JARs.
  • Add the JAR by navigating into its JAR location.

Executable Application for Generating a Primary keys using Sequence Generator

JPAImpl.java


package net.javabeat.eclipselink;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

import net.javabeat.eclipselink.data.Address;
import net.javabeat.eclipselink.data.AddressPK;
import net.javabeat.eclipselink.data.ContractDeveloper;
import net.javabeat.eclipselink.data.Developer;
import net.javabeat.eclipselink.data.DriverLicense;
import net.javabeat.eclipselink.data.Employee;
import net.javabeat.eclipselink.data.EmployeePeriod;
import net.javabeat.eclipselink.data.FreelanceDeveloper;
import net.javabeat.eclipselink.data.GlobalProject;
import net.javabeat.eclipselink.data.ICDLComputerLicense;
import net.javabeat.eclipselink.data.LocalProject;
import net.javabeat.eclipselink.data.Phone;
import net.javabeat.eclipselink.data.PhonePK;
import net.javabeat.eclipselink.data.Project;

public class JPAImpl {
 static EntityManagerFactory factory = null;
 static EntityManager em = null;
 static {
 factory = Persistence.createEntityManagerFactory("EclipseLink-JPA-OracleDS");
 em = factory.createEntityManager();
 }

 public static void main(String [] args){
 // Begin a Transaction
 em.getTransaction().begin();
 // Create a Driver License
 createDriverLicense();
 // Commit
 em.getTransaction().commit();
 }

 public static void createDriverLicense(){
 DriverLicense license = new DriverLicense(); // Create a driver license
 license.setDriverLicenseName("All Vehicles License"); // Set License Name
 license.setDriverLicenseIssueDate(new Date()); // Anonymous date
 license.setDriverLicenseExpiryDate(new Date()); // Anonymous date
 em.persist(license);
 }

}

  • The DriverLicense entity doesn’t provide a primary key as you’ve noted in the provided examples at the EclipseTutorial.
  • The primary keys are generated automatically using a SequenceGenetator that defined in the License mapped superclass.

The Persisted Records Using Sequence Generator

Figure 1.2 shows you the records that persisted into the Oracle database using a sequence generator. License Records Using a Sequence Generator

Figure 1.2

Primary Key Generation Using Table Generator

At this example, you would be seeing a database table for generating a primary keys.

Anatomy of @TableGenerator

The @TableGenerator annotation defines a primary key generator that may be referenced by name when a generator element is specified  for the @GeneratedValue. A Table generator may be specified on the entity class or on the primary key field or property.

  • Target: Type, Method and Field
  • Uses:@TableGenerator
  • Argument:
    1. name (Required): A unique generator name that can be referenced by one or more classes to be the generator for id values.
    2. table (Optional): Name of table that stores the generated for id values.
    3. catalog (Optional): The catalog of the table.
    4. schema (Optional): The schema of the table.
    5. pKColumnName (Optional): Name of the primary key column in the table.
    6. valueColumnName (Optional): Name of the column that stores the last value generated.
    7. pKColumnValue (Optional): The primary key value in the generator table that distinguishes this set of generated values from others that may be stored in the table.
    8. initialValue (Optional): The value used to initialize the column that stores the last value generated.
    9. allocationSize (Optional): The amount to increment by when allocating id numbers from the generator.
    10. uniqueConstraints (Optional): Unique constraints that are to be placed on the table.

Database Table Sequence Creation

MySQL database is capable to create table sequence, so at this section of primary key generation we would return back into the previous examples that already made using the MySQL database. Figure 1.0 and 1.1 shows you the  Table sequence and DriverLicense Table that created on the MySQL database respectively.

Table Sequence

Figure 1.0

Previous Created Driver License

Figure 1.1

  • The Table sequence that created contains two columns one for sequence name and the other for sequence number.
  • Nothing changed on drive licenses table.

License Entity Uses a Table Sequence Generator

The following License entity does use the Table sequence generator for generating the primary keys for the entities that created from the License (in case the license isn’t mapped superclass) and from any entity belongs to the License inheritance. License.java


package net.javabeat.eclipselink.data;

import javax.persistence.CascadeType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.MappedSuperclass;
import javax.persistence.TableGenerator;

@MappedSuperclass
public abstract class License {

@Id
 @GeneratedValue(generator="LICENSE_TABLE_SEQ",strategy=GenerationType.TABLE)
 @TableGenerator(name="LICENSE_TABLE_SEQ",
 table="sequences",
 pkColumnName="SEQ_NAME", // Specify the name of the column of the primary key
 valueColumnName="SEQ_NUMBER", // Specify the name of the column that stores the last value generated
 pkColumnValue="LICENSE_ID", // Specify the primary key column value that would be considered as a primary key generator
 allocationSize=1)
 protected int licenseId;

@ManyToOne(cascade = CascadeType.ALL)
 @JoinColumn(name = "employeeId")
 private Employee employee;

public Employee getEmployee() {
 return employee;
 }

public void setEmployee(Employee employee) {
 this.employee = employee;
 }

public int getLicenseId() {
 return licenseId;
 }

public void setLicenseId(int licenseId) {
 this.licenseId = licenseId;
 }
}

DriverLicense.java


package net.javabeat.eclipselink.data;

import java.util.Date;

import javax.persistence.Entity;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

@Entity
@Table(name="driverlicenses")
public class DriverLicense extends License{

 private String driverLicenseName;
 @Temporal(TemporalType.DATE)
 private Date driverLicenseExpiryDate;
 @Temporal(TemporalType.DATE)
 private Date driverLicenseIssueDate;

 public String getDriverLicenseName() {
 return driverLicenseName;
 }
 public void setDriverLicenseName(String driverLicenseName) {
 this.driverLicenseName = driverLicenseName;
 }
 public Date getDriverLicenseExpiryDate() {
 return driverLicenseExpiryDate;
 }
 public void setDriverLicenseExpiryDate(Date driverLicenseExpiryDate) {
 this.driverLicenseExpiryDate = driverLicenseExpiryDate;
 }
 public Date getDriverLicenseIssueDate() {
 return driverLicenseIssueDate;
 }
 public void setDriverLicenseIssueDate(Date driverLicenseIssueDate) {
 this.driverLicenseIssueDate = driverLicenseIssueDate;
 }
}

Required Persistence Configuration

<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
 <persistence-unit name="EclipseLink-JPA-Installation" transaction-type="RESOURCE_LOCAL">

<!-- Some Entities have omit intentionally -->

<class>net.javabeat.eclipselink.data.License</class>
 <class>net.javabeat.eclipselink.data.DriverLicense</class>
<class>net.javabeat.eclipselink.data.Employee</class>
 <class>net.javabeat.eclipselink.data.ICDLComputerLicense</class>
 <properties>
 <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/JavaBeat"/>
 <property name="javax.persistence.jdbc.user" value="root"/>
 <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
 <property name="javax.persistence.jdbc.password" value="root"/>
 <property name="eclipselink.logging.level" value="FINEST"/>
 <property name="" value=""/>
 </properties>
 </persistence-unit>
  • If you’ve never ever see the previous entities before and you would learn more about it, refer to the EclipseLink Tutorial.
  • The DriverLicense is mentioned in the persistence.xml
  • The License mapped superclass is mentioned in the persistence.xml
  • No need to mention the generator of the primary keys.

Executable Application for Generating a Primary keys using Table Sequence Generator

JPAImpl.java


package net.javabeat.eclipselink;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

import net.javabeat.eclipselink.data.Address;
import net.javabeat.eclipselink.data.AddressPK;
import net.javabeat.eclipselink.data.ContractDeveloper;
import net.javabeat.eclipselink.data.Developer;
import net.javabeat.eclipselink.data.DriverLicense;
import net.javabeat.eclipselink.data.Employee;
import net.javabeat.eclipselink.data.EmployeePeriod;
import net.javabeat.eclipselink.data.FreelanceDeveloper;
import net.javabeat.eclipselink.data.GlobalProject;
import net.javabeat.eclipselink.data.ICDLComputerLicense;
import net.javabeat.eclipselink.data.LocalProject;
import net.javabeat.eclipselink.data.Phone;
import net.javabeat.eclipselink.data.PhonePK;
import net.javabeat.eclipselink.data.Project;

public class JPAImpl {
 static EntityManagerFactory factory = null;
 static EntityManager em = null;
 static {
 factory = Persistence.createEntityManagerFactory("EclipseLink-JPA-Installation");
 em = factory.createEntityManager();
 }

 public static void main(String [] args){
 // Begin a Transaction
 em.getTransaction().begin();
 // Find the Employee
 Employee employee = em.find(Employee.class, 1);
 // Create a Driver License
 createDriverLicense(employee);
 // Commit
 em.getTransaction().commit();
 }

 public static void createDriverLicense(Employee employee){
 DriverLicense license = new DriverLicense(); // Create a driver license
 license.setDriverLicenseName("All Vehicles License"); // Set License Name
 license.setDriverLicenseIssueDate(new Date()); // Anonymous date
 license.setDriverLicenseExpiryDate(new Date()); // Anonymous date
 license.setEmployee(employee);
 em.persist(license);
 }

}

The Persisted Records Using Table Sequence Generator

Figure 1.2 shows you the records that persisted into the Oracle database using a Table sequence generator. Records Persisted Using Table Sequence

Figure 1.2

The impact of adding three records of driver license on the Table sequence is shown at the Figure 1.3

Impact of adding DriverLicenses on Table Sequence

Figure 1.3

  • If you’ve noted about the SEQ_NUMBER, it’s value is 3 cause this is the last number that has been generated.

IDENTITY & AUTO Generators

The IDENTITY value specify the use of an identify column, so if you’ve noted the License mapped superclass, you should be able see the licenseId as an identified property. The IDENTITY strategy is one of the most simple strategy that could be applied. Let’s look at the License entity if we’ve decided to use the IDENTITY strategy. License.java


package net.javabeat.eclipselink.data;

import javax.persistence.CascadeType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.MappedSuperclass;

@MappedSuperclass
public abstract class License {

@Id
 @GeneratedValue(strategy=GenerationType.IDENTITY)
 protected int licenseId;

@ManyToOne(cascade = CascadeType.ALL)
 @JoinColumn(name = "employeeId")
 private Employee employee;

public Employee getEmployee() {
 return employee;
 }

public void setEmployee(Employee employee) {
 this.employee = employee;
 }

public int getLicenseId() {
 return licenseId;
 }

public void setLicenseId(int licenseId) {
 this.licenseId = licenseId;
 }
}

  • The primary key of the License entity should be annotated using @GeneratedValue, but this time with an IDENTITY as generator.

Also, your database engine should support the AUTO_INCREMENT principle. Figure 1.4 shows you the updated licenseId column. Auto Increment Selected

Figure 1.4

  • The licenseId primary key is updated to be Auto Increment.

That’s what you have to do if you’ve selected and Identity as a primary key generator. Now let’s execute the previous executable JPAImpl. The JPAImpl will execute smoothly without change, that’s because the IDENTITY needs no more that an auto incremental primary key column. See Figure 1.5 that shows you an additional records that persisted using IDENTITY. Persisted Records Using Identity

Figure 1.5

AUTO value indicates that the persistence provider should pick an appropriate strategy for the particular database, in case we’ve used a MySQL and annotate the License entity using Auto, we almost probably getting such that exception.


Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Table 'javabeat.sequence' doesn't exist
 at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
 at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
 at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
 at java.lang.reflect.Constructor.newInstance(Unknown Source)
 at com.mysql.jdbc.Util.handleNewInstance(Util.java:411)
 at com.mysql.jdbc.Util.getInstance(Util.java:386)
 at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1054)
 at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4120)
 at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4052)
 at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2503)
 at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2664)
 at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2815)
 at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:2155)
 at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2458)
 at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2375)
 at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2359)
 at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeDirectNoSelect(DatabaseAccessor.java:890)
 ... 32 more

It’s clear that the JPA select sequence as a primary key strategy for generating a primary keys. In general Auto generation may expect a database resource to exist, or it may attempt to create one. A vendor may provide dcoumention on how to create such resources in the event that it doesn’t support schema generation or cannot create schema resource at runtime.

Summary

This tutorial is intended to explain the different ways of generating a primary keys. We’ve already clarified the using of @GeneratedValue annotation and the different possible values that could occurred within it. The JPA provides a lot of generator that ranging from simple to complex. The most simple generators that could be used are IDENTITY and AUTO, meanwhile the most complex generators that are used a little more work to achieve are SEQUENCE and TABLE SEQUENCE.

Comments

comments

About Amr Mohammed

Speak Your Mind

*