This tutorial is continued part 1.
The next strategy we want to talk is the GenerationType.SEQUENCE strategy
You declare this strategy in the Clazz entity as follows:
1 2 3 |
@Id @GeneratedValue(strategy = GenerationType.SEQUENCE) private Long id; |
Similar to GenerationType.TABLE strategy, GenerationType.SEQUENCE strategy also depends on the value of “hibernate.id.new.generator_mappings” property in the JPA configuration file that is true or false.
If the value of this property is false, the generated values for the primary key column will be based on the database sequence of the database that we are using. On the other hand, you can imagine the database sequence is the object that holds the information of the primary key columns in the tables. When we insert a new record, this sequence will return the value of the primary key column for the record, then we can insert this record. Currently, MySQL does not support database sequence so in this case, when I run the example, I have the following error:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
Exception in thread "main" java.lang.NullPointerException at java.util.StringTokenizer.<init>(StringTokenizer.java:199) at org.hibernate.engine.jdbc.internal.BasicFormatterImpl$FormatProcess.<init>(BasicFormatterImpl.java:100) at org.hibernate.engine.jdbc.internal.BasicFormatterImpl.format(BasicFormatterImpl.java:75) at org.hibernate.engine.jdbc.spi.SqlStatementLogger.logStatement(SqlStatementLogger.java:89) at org.hibernate.engine.jdbc.spi.SqlStatementLogger.logStatement(SqlStatementLogger.java:77) at org.hibernate.engine.jdbc.internal.StatementPreparerImpl$StatementPreparationTemplate.prepareStatement(StatementPreparerImpl.java:167) at org.hibernate.engine.jdbc.internal.StatementPreparerImpl.prepareStatement(StatementPreparerImpl.java:72) at org.hibernate.id.SequenceGenerator.generateHolder(SequenceGenerator.java:114) at org.hibernate.id.SequenceHiLoGenerator$1.getNextValue(SequenceHiLoGenerator.java:71) at org.hibernate.id.enhanced.LegacyHiLoAlgorithmOptimizer.generate(LegacyHiLoAlgorithmOptimizer.java:60) at org.hibernate.id.SequenceHiLoGenerator.generate(SequenceHiLoGenerator.java:67) at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:105) at org.hibernate.jpa.event.internal.core.JpaPersistEventListener.saveWithGeneratedId(JpaPersistEventListener.java:67) at org.hibernate.event.internal.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:189) at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:132) at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:58) at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:782) at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:767) at com.huongdanjava.jpageneratedvalue.Application.main(Application.java:19) |
If the value of the “hibernate.id.new_generator_mappings” property is true, Hibernate will use a default table named hibernate_sequence, noting that sequence without “s” character. The purpose of this table is to store information for generating the value of the primary key column in the clazz table. The structure of this table is as follows:
1 2 3 4 5 |
CREATE TABLE `hibernate_sequence` ( `sequence_name` VARCHAR(255) NOT NULL, `next_val` INT(19), PRIMARY KEY (`sequence_name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; |
At this point, if you run your example, you will encounter the following error:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
ERROR: could not read a hi value - you need to populate the table: hibernate_sequence Exception in thread "main" javax.persistence.PersistenceException: org.hibernate.id.IdentifierGenerationException: could not read a hi value - you need to populate the table: hibernate_sequence at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:149) at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:157) at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:164) at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:789) at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:767) at com.huongdanjava.jpageneratedvalue.Application.main(Application.java:19) Caused by: org.hibernate.id.IdentifierGenerationException: could not read a hi value - you need to populate the table: hibernate_sequence at org.hibernate.id.enhanced.TableStructure$1$1.execute(TableStructure.java:142) at org.hibernate.id.enhanced.TableStructure$1$1.execute(TableStructure.java:126) at org.hibernate.jdbc.WorkExecutor.executeReturningWork(WorkExecutor.java:55) at org.hibernate.jdbc.AbstractReturningWork.accept(AbstractReturningWork.java:34) at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcIsolationDelegate.delegateWork(JdbcIsolationDelegate.java:57) at org.hibernate.id.enhanced.TableStructure$1.getNextValue(TableStructure.java:125) at org.hibernate.id.enhanced.NoopOptimizer.generate(NoopOptimizer.java:40) at org.hibernate.id.enhanced.SequenceStyleGenerator.generate(SequenceStyleGenerator.java:452) at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:105) at org.hibernate.jpa.event.internal.core.JpaPersistEventListener.saveWithGeneratedId(JpaPersistEventListener.java:67) at org.hibernate.event.internal.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:189) at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:132) at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:58) at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:782) ... 2 more |
The reason is that in order to use the hibernate_sequence table, we need to insert the initial value for the Clazz entity with the sequence_name is Clazz and next_val is 1.
1 |
INSERT INTO hibernate_sequence SET sequence_name='Clazz', next_val=1; |
Run the example again, you will see the results as follows:
As you can see, here I have run 2 times and each time the value of the next_val column in hibernate_sequence table increases to 1 unit. At first, when no record is available, a new record will be inserted into the hibernate_sequence table with the value next_val of 2. The value of the primary key column in the clazz table also increases to 1 unit respectively.
We can also change the default Hibernate table for this strategy using the @SequenceGenerator annotation and declare it like this:
1 2 3 4 |
@Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequence_gen") @SequenceGenerator(name = "sequence_gen", sequenceName = "sequence", allocationSize = 2) private Long id; |
In the @SequenceGenerator annotation, the sequenceName attribute with the name of the table we want to use, the structure of this table is similar to the Hibernate default hibernate_sequence table:
1 2 3 4 5 |
CREATE TABLE `sequence` ( `sequence_name` VARCHAR(255) NOT NULL, `next_val` INT(19), PRIMARY KEY (`sequence_name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; |
allocationSize is similar to GenerationType.TABLE strategy.
The important part is that the value of the name attribute in the @SequenceGenerator annotation will be the value of the generator attribute in the @GeneratedValue annotation.
Result:
Next, we’ll talk about strategy GenerationType.IDENTITY
This is the convenient and easiest strategy. You declare this strategy in the Clazz entity as follows:
1 2 3 |
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; |
This strategy is used to take advantage of some types of databases that support self-generated values for the primary key column. For example, in this tutorial, we define the primary key id of the table clazz with AUTO_INCREMENT. With this definition, whenever we add a new record to the clazz table, MySQL will help us to generate new values for the primary key column without having to do anything.
No need to define another table so I strongly encourage you to use this strategy if your database which is using, support 😀
The final strategy and also the default strategy of the @GeneratedValue annotation is GenerationType.AUTO
Since it is the default strategy, you do not need to declare it explicitly.
1 2 3 |
@Id @GeneratedValue private Long id; |
If you want, you can also declare explicitly as follows:
1 2 3 |
@Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; |
Generally, we will use GenerationType.AUTO when we need to generate the value for the primary key column without having to consider how the value of the primary key column will be generated. JPA can then choose any one of the 3 strategies we have discussed above.