hibernate default value of transient member - java

I have a transient boolean member that should be set to true when I retrieve the entity from db, but it should be false when I create a new object until it is persisted. After it is saved to db the member should become true.
There is a way to make hibernate manage this automatically or should I do it in the manager?
Hope the following code will clarify my needs.
class MyEntity(){
#Id
Integer id;
#Transient
boolean required;
}
class Test(){
MyEntity entity1 = new MyEntity();
assertFalse(entity1.required);
persist(entity1);
assertTrue(entity1.required);
MyEntity entityAlreadySaved = getMyEntityWithID(1);
assertTrue(entityAlreadySaved.required);
}

Basicaly Hibernate wont handle #Transient fields. So you have to handle yourself.
by default boolean variable holds false. if you want to set it ture then you have to wirte logic based on Adam answer
for example
class MyEntity{
#Id
Integer id;
#Transient
boolean required;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public boolean isRequired() {
if (getId() == null){
setRequired(false);
}else{
setRequired(true);
}
return required;
}
public void setRequired(boolean required) {
this.required = required;
}
}
And you have to handle delete logic seperatly

You don't need this field since you can tell from its id if an entity is already persisted or not. If the id is null it is new otherwise it is not.
You just need a method if you really need it which checks the existence of the id field.
In most cases it is recommended to use #GeneratedValue next to #Id so Hibernate will handle ids for you.
If you really need that field use the id field to set up its values either in the constructor or the setter methods (depends on what Hibernate uses).

Related

Spring + Hibernate wrong entity values on save [duplicate]

Is it possible to use a DB sequence for some column that is not the identifier/is not part of a composite identifier?
I'm using hibernate as jpa provider, and I have a table that has some columns that are generated values (using a sequence), although they are not part of the identifier.
What I want is to use a sequence to create a new value for an entity, where the column for the sequence is NOT (part of) the primary key:
#Entity
#Table(name = "MyTable")
public class MyEntity {
//...
#Id //... etc
public Long getId() {
return id;
}
//note NO #Id here! but this doesn't work...
#GeneratedValue(strategy = GenerationType.AUTO, generator = "myGen")
#SequenceGenerator(name = "myGen", sequenceName = "MY_SEQUENCE")
#Column(name = "SEQ_VAL", unique = false, nullable = false, insertable = true, updatable = true)
public Long getMySequencedValue(){
return myVal;
}
}
Then when I do this:
em.persist(new MyEntity());
the id will be generated, but the mySequenceVal property will be also generated by my JPA provider.
Just to make things clear: I want Hibernate to generate the value for the mySequencedValue property. I know Hibernate can handle database-generated values, but I don't want to use a trigger or any other thing other than Hibernate itself to generate the value for my property. If Hibernate can generate values for primary keys, why can't it generate for a simple property?
Looking for answers to this problem, I stumbled upon this link
It seems that Hibernate/JPA isn't able to automatically create a value for your non-id-properties. The #GeneratedValue annotation is only used in conjunction with #Id to create auto-numbers.
The #GeneratedValue annotation just tells Hibernate that the database is generating this value itself.
The solution (or work-around) suggested in that forum is to create a separate entity with a generated Id, something like this:
#Entity
public class GeneralSequenceNumber {
#Id
#GeneratedValue(...)
private Long number;
}
#Entity
public class MyEntity {
#Id ..
private Long id;
#OneToOne(...)
private GeneralSequnceNumber myVal;
}
I found that #Column(columnDefinition="serial") works perfect but only for PostgreSQL. For me this was perfect solution, because second entity is "ugly" option.
A call to saveAndFlush on the entity is also necessary, and save won't be enough to populate the value from the DB.
I know this is a very old question, but it's showed firstly upon the results and jpa has changed a lot since the question.
The right way to do it now is with the #Generated annotation. You can define the sequence, set the default in the column to that sequence and then map the column as:
#Generated(GenerationTime.INSERT)
#Column(name = "column_name", insertable = false)
Hibernate definitely supports this. From the docs:
"Generated properties are properties which have their values generated by the database. Typically, Hibernate applications needed to refresh objects which contain any properties for which the database was generating values. Marking properties as generated, however, lets the application delegate this responsibility to Hibernate. Essentially, whenever Hibernate issues an SQL INSERT or UPDATE for an entity which has defined generated properties, it immediately issues a select afterwards to retrieve the generated values."
For properties generated on insert only, your property mapping (.hbm.xml) would look like:
<property name="foo" generated="insert"/>
For properties generated on insert and update your property mapping (.hbm.xml) would look like:
<property name="foo" generated="always"/>
Unfortunately, I don't know JPA, so I don't know if this feature is exposed via JPA (I suspect possibly not)
Alternatively, you should be able to exclude the property from inserts and updates, and then "manually" call session.refresh( obj ); after you have inserted/updated it to load the generated value from the database.
This is how you would exclude the property from being used in insert and update statements:
<property name="foo" update="false" insert="false"/>
Again, I don't know if JPA exposes these Hibernate features, but Hibernate does support them.
I fixed the generation of UUID (or sequences) with Hibernate using #PrePersist annotation:
#PrePersist
public void initializeUUID() {
if (uuid == null) {
uuid = UUID.randomUUID().toString();
}
}
Looks like thread is old, I just wanted to add my solution here(Using AspectJ - AOP in spring).
Solution is to create a custom annotation #InjectSequenceValue as follows.
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
public #interface InjectSequenceValue {
String sequencename();
}
Now you can annotate any field in entity, so that the underlying field (Long/Integer) value will be injected at runtime using the nextvalue of the sequence.
Annotate like this.
//serialNumber will be injected dynamically, with the next value of the serialnum_sequence.
#InjectSequenceValue(sequencename = "serialnum_sequence")
Long serialNumber;
So far we have marked the field we need to inject the sequence value.So we will look how to inject the sequence value to the marked fields, this is done by creating the point cut in AspectJ.
We will trigger the injection just before the save/persist method is being executed.This is done in the below class.
#Aspect
#Configuration
public class AspectDefinition {
#Autowired
JdbcTemplate jdbcTemplate;
//#Before("execution(* org.hibernate.session.save(..))") Use this for Hibernate.(also include session.save())
#Before("execution(* org.springframework.data.repository.CrudRepository.save(..))") //This is for JPA.
public void generateSequence(JoinPoint joinPoint){
Object [] aragumentList=joinPoint.getArgs(); //Getting all arguments of the save
for (Object arg :aragumentList ) {
if (arg.getClass().isAnnotationPresent(Entity.class)){ // getting the Entity class
Field[] fields = arg.getClass().getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(InjectSequenceValue.class)) { //getting annotated fields
field.setAccessible(true);
try {
if (field.get(arg) == null){ // Setting the next value
String sequenceName=field.getAnnotation(InjectSequenceValue.class).sequencename();
long nextval=getNextValue(sequenceName);
System.out.println("Next value :"+nextval); //TODO remove sout.
field.set(arg, nextval);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
}
/**
* This method fetches the next value from sequence
* #param sequence
* #return
*/
public long getNextValue(String sequence){
long sequenceNextVal=0L;
SqlRowSet sqlRowSet= jdbcTemplate.queryForRowSet("SELECT "+sequence+".NEXTVAL as value FROM DUAL");
while (sqlRowSet.next()){
sequenceNextVal=sqlRowSet.getLong("value");
}
return sequenceNextVal;
}
}
Now you can annotate any Entity as below.
#Entity
#Table(name = "T_USER")
public class UserEntity {
#Id
#SequenceGenerator(sequenceName = "userid_sequence",name = "this_seq")
#GeneratedValue(strategy = GenerationType.SEQUENCE,generator = "this_seq")
Long id;
String userName;
String password;
#InjectSequenceValue(sequencename = "serialnum_sequence") // this will be injected at the time of saving.
Long serialNumber;
String name;
}
As a followup here's how I got it to work:
#Override public Long getNextExternalId() {
BigDecimal seq =
(BigDecimal)((List)em.createNativeQuery("select col_msd_external_id_seq.nextval from dual").getResultList()).get(0);
return seq.longValue();
}
If you are using postgresql
And i'm using in spring boot 1.5.6
#Column(columnDefinition = "serial")
#Generated(GenerationTime.INSERT)
private Integer orderID;
Although this is an old thread I want to share my solution and hopefully get some feedback on this. Be warned that I only tested this solution with my local database in some JUnit testcase. So this is not a productive feature so far.
I solved that issue for my by introducing a custom annotation called Sequence with no property. It's just a marker for fields that should be assigned a value from an incremented sequence.
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
public #interface Sequence
{
}
Using this annotation i marked my entities.
public class Area extends BaseEntity implements ClientAware, IssuerAware
{
#Column(name = "areaNumber", updatable = false)
#Sequence
private Integer areaNumber;
....
}
To keep things database independent I introduced an entity called SequenceNumber which holds the sequence current value and the increment size. I chose the className as unique key so each entity class wil get its own sequence.
#Entity
#Table(name = "SequenceNumber", uniqueConstraints = { #UniqueConstraint(columnNames = { "className" }) })
public class SequenceNumber
{
#Id
#Column(name = "className", updatable = false)
private String className;
#Column(name = "nextValue")
private Integer nextValue = 1;
#Column(name = "incrementValue")
private Integer incrementValue = 10;
... some getters and setters ....
}
The last step and the most difficult is a PreInsertListener that handles the sequence number assignment. Note that I used spring as bean container.
#Component
public class SequenceListener implements PreInsertEventListener
{
private static final long serialVersionUID = 7946581162328559098L;
private final static Logger log = Logger.getLogger(SequenceListener.class);
#Autowired
private SessionFactoryImplementor sessionFactoryImpl;
private final Map<String, CacheEntry> cache = new HashMap<>();
#PostConstruct
public void selfRegister()
{
// As you might expect, an EventListenerRegistry is the place with which event listeners are registered
// It is a service so we look it up using the service registry
final EventListenerRegistry eventListenerRegistry = sessionFactoryImpl.getServiceRegistry().getService(EventListenerRegistry.class);
// add the listener to the end of the listener chain
eventListenerRegistry.appendListeners(EventType.PRE_INSERT, this);
}
#Override
public boolean onPreInsert(PreInsertEvent p_event)
{
updateSequenceValue(p_event.getEntity(), p_event.getState(), p_event.getPersister().getPropertyNames());
return false;
}
private void updateSequenceValue(Object p_entity, Object[] p_state, String[] p_propertyNames)
{
try
{
List<Field> fields = ReflectUtil.getFields(p_entity.getClass(), null, Sequence.class);
if (!fields.isEmpty())
{
if (log.isDebugEnabled())
{
log.debug("Intercepted custom sequence entity.");
}
for (Field field : fields)
{
Integer value = getSequenceNumber(p_entity.getClass().getName());
field.setAccessible(true);
field.set(p_entity, value);
setPropertyState(p_state, p_propertyNames, field.getName(), value);
if (log.isDebugEnabled())
{
LogMF.debug(log, "Set {0} property to {1}.", new Object[] { field, value });
}
}
}
}
catch (Exception e)
{
log.error("Failed to set sequence property.", e);
}
}
private Integer getSequenceNumber(String p_className)
{
synchronized (cache)
{
CacheEntry current = cache.get(p_className);
// not in cache yet => load from database
if ((current == null) || current.isEmpty())
{
boolean insert = false;
StatelessSession session = sessionFactoryImpl.openStatelessSession();
session.beginTransaction();
SequenceNumber sequenceNumber = (SequenceNumber) session.get(SequenceNumber.class, p_className);
// not in database yet => create new sequence
if (sequenceNumber == null)
{
sequenceNumber = new SequenceNumber();
sequenceNumber.setClassName(p_className);
insert = true;
}
current = new CacheEntry(sequenceNumber.getNextValue() + sequenceNumber.getIncrementValue(), sequenceNumber.getNextValue());
cache.put(p_className, current);
sequenceNumber.setNextValue(sequenceNumber.getNextValue() + sequenceNumber.getIncrementValue());
if (insert)
{
session.insert(sequenceNumber);
}
else
{
session.update(sequenceNumber);
}
session.getTransaction().commit();
session.close();
}
return current.next();
}
}
private void setPropertyState(Object[] propertyStates, String[] propertyNames, String propertyName, Object propertyState)
{
for (int i = 0; i < propertyNames.length; i++)
{
if (propertyName.equals(propertyNames[i]))
{
propertyStates[i] = propertyState;
return;
}
}
}
private static class CacheEntry
{
private int current;
private final int limit;
public CacheEntry(final int p_limit, final int p_current)
{
current = p_current;
limit = p_limit;
}
public Integer next()
{
return current++;
}
public boolean isEmpty()
{
return current >= limit;
}
}
}
As you can see from the above code the listener used one SequenceNumber instance per entity class and reserves a couple of sequence numbers defined by the incrementValue of the SequenceNumber entity. If it runs out of sequence numbers it loads the SequenceNumber entity for the target class and reserves incrementValue values for the next calls. This way I do not need to query the database each time a sequence value is needed.
Note the StatelessSession that is being opened for reserving the next set of sequence numbers. You cannot use the same session the target entity is currently persisted since this would lead to a ConcurrentModificationException in the EntityPersister.
Hope this helps someone.
I run in the same situation like you and I also didn't find any serious answers if it is basically possible to generate non-id propertys with JPA or not.
My solution is to call the sequence with a native JPA query to set the property by hand before persisiting it.
This is not satisfying but it works as a workaround for the moment.
Mario
I've found this specific note in session 9.1.9 GeneratedValue Annotation from JPA specification:
"[43] Portable applications should not use the GeneratedValue annotation on other persistent fields or properties."
So, I presume that it is not possible to auto generate value for non primary key values at least using simply JPA.
You can do exactly what you are asking.
I've found it is possible to adapt Hibernate's IdentifierGenerator implementations by registering them with an Integrator. With this you should be able to use any id sequence generator provided by Hibernate to generate sequences for non-id fields (presumably the non-sequential id generators would work as well).
There are quite a few options for generating ids this way. Check out some of the implementations of IdentifierGenerator, specifically SequenceStyleGenerator and TableGenerator. If you have configured generators using the #GenericGenerator annotation, then the parameters for these classes may be familiar to you. This would also have the advantage of using Hibernate to generate the SQL.
Here is how I got it working:
import org.hibernate.Session;
import org.hibernate.boot.Metadata;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.id.enhanced.TableGenerator;
import org.hibernate.integrator.spi.Integrator;
import org.hibernate.internal.SessionImpl;
import org.hibernate.service.spi.SessionFactoryServiceRegistry;
import org.hibernate.tuple.ValueGenerator;
import org.hibernate.type.LongType;
import java.util.Properties;
public class SequenceIntegrator implements Integrator, ValueGenerator<Long> {
public static final String TABLE_NAME = "SEQUENCE_TABLE";
public static final String VALUE_COLUMN_NAME = "NEXT_VAL";
public static final String SEGMENT_COLUMN_NAME = "SEQUENCE_NAME";
private static SessionFactoryServiceRegistry serviceRegistry;
private static Metadata metadata;
private static IdentifierGenerator defaultGenerator;
#Override
public void integrate(Metadata metadata, SessionFactoryImplementor sessionFactoryImplementor, SessionFactoryServiceRegistry sessionFactoryServiceRegistry) {
//assigning metadata and registry to fields for use in a below example
SequenceIntegrator.metadata = metadata;
SequenceIntegrator.serviceRegistry = sessionFactoryServiceRegistry;
SequenceIntegrator.defaultGenerator = getTableGenerator(metadata, sessionFactoryServiceRegistry, "DEFAULT");
}
private TableGenerator getTableGenerator(Metadata metadata, SessionFactoryServiceRegistry sessionFactoryServiceRegistry, String segmentValue) {
TableGenerator generator = new TableGenerator();
Properties properties = new Properties();
properties.setProperty("table_name", TABLE_NAME);
properties.setProperty("value_column_name", VALUE_COLUMN_NAME);
properties.setProperty("segment_column_name", SEGMENT_COLUMN_NAME);
properties.setProperty("segment_value", segmentValue);
//any type should work if the generator supports it
generator.configure(LongType.INSTANCE, properties, sessionFactoryServiceRegistry);
//this should create the table if ddl auto update is enabled and if this function is called inside of the integrate method
generator.registerExportables(metadata.getDatabase());
return generator;
}
#Override
public Long generateValue(Session session, Object o) {
// registering additional generators with getTableGenerator will work here. inserting new sequences can be done dynamically
// example:
// TableGenerator classSpecificGenerator = getTableGenerator(metadata, serviceRegistry, o.getClass().getName());
// return (Long) classSpecificGenerator.generate((SessionImpl)session, o);
return (Long) defaultGenerator.generate((SessionImpl)session, o);
}
#Override
public void disintegrate(SessionFactoryImplementor sessionFactoryImplementor, SessionFactoryServiceRegistry sessionFactoryServiceRegistry) {
}
}
You would need to register this class in the META-INF/services directory. Here is what the Hibernate documentation has to say about registering an Integrator:
For the integrator to be automatically used when Hibernate starts up, you will need to add a META-INF/services/org.hibernate.integrator.spi.Integrator file to your jar. The file should contain the fully qualified name of the class implementing the interface.
Because this class implements the ValueGenerator class, it can be used with the #GeneratorType annotation to automatically generate the sequential values. Here is how your class might be configured:
#Entity
#Table(name = "MyTable")
public class MyEntity {
//...
#Id //... etc
public Long getId() {
return id;
}
#GeneratorType(type = SequenceIntegrator.class, when = GenerationTime.INSERT)
#Column(name = "SEQ_VAL", unique = false, nullable = false, insertable = true, updatable = true)
public Long getMySequencedValue(){
return myVal;
}
}
I want to provide an alternative next to #Morten Berg's accepted solution, which worked better for me.
This approach allows to define the field with the actually desired Number type - Long in my use case - instead of GeneralSequenceNumber. This can be useful, e.g. for JSON (de-)serialization.
The downside is that it requires a little more database overhead.
First, we need an ActualEntity in which we want to auto-increment generated of type Long:
// ...
#Entity
public class ActualEntity {
#Id
// ...
Long id;
#Column(unique = true, updatable = false, nullable = false)
Long generated;
// ...
}
Next, we need a helper entity Generated. I placed it package-private next to ActualEntity, to keep it an implementation detail of the package:
#Entity
class Generated {
#Id
#GeneratedValue(strategy = SEQUENCE, generator = "seq")
#SequenceGenerator(name = "seq", initialValue = 1, allocationSize = 1)
Long id;
}
Finally, we need a place to hook in right before we save the ActualEntity. There, we create and persist aGenerated instance. This then provides a database-sequence generated id of type Long. We make use of this value by writing it to ActualEntity.generated.
In my use case, I implemented this using a Spring Data REST #RepositoryEventHandler, which get's called right before the ActualEntity get's persisted. It should demonstrate the principle:
#Component
#RepositoryEventHandler
public class ActualEntityHandler {
#Autowired
EntityManager entityManager;
#Transactional
#HandleBeforeCreate
public void generate(ActualEntity entity) {
Generated generated = new Generated();
entityManager.persist(generated);
entity.setGlobalId(generated.getId());
entityManager.remove(generated);
}
}
I didn't test it in a real-life application, so please enjoy with care.
"I don't want to use a trigger or any other thing other than Hibernate itself to generate the value for my property"
In that case, how about creating an implementation of UserType which generates the required value, and configuring the metadata to use that UserType for persistence of the mySequenceVal property?
This is not the same as using a sequence. When using a sequence, you are not inserting or updating anything. You are simply retrieving the next sequence value. It looks like hibernate does not support it.
If you have a column with UNIQUEIDENTIFIER type and default generation needed on insert but column is not PK
#Generated(GenerationTime.INSERT)
#Column(nullable = false , columnDefinition="UNIQUEIDENTIFIER")
private String uuidValue;
In db you will have
CREATE TABLE operation.Table1
(
Id INT IDENTITY (1,1) NOT NULL,
UuidValue UNIQUEIDENTIFIER DEFAULT NEWID() NOT NULL)
In this case you will not define generator for a value which you need (It will be automatically thanks to columnDefinition="UNIQUEIDENTIFIER"). The same you can try for other column types
I have found a workaround for this on MySql databases using #PostConstruct and JdbcTemplate in a Spring application. It may be doable with other databases but the use case that I will present is based on my experience with MySql, as it uses auto_increment.
First, I had tried defining a column as auto_increment using the ColumnDefinition property of the #Column annotation, but it was not working as the column needed to be an key in order to be auto incremental, but apparently the column wouldn't be defined as an index until after it was defined, causing a deadlock.
Here is where I came with the idea of creating the column without the auto_increment definition, and adding it after the database was created. This is possible using the #PostConstruct annotation, which causes a method to be invoked right after the application has initialized the beans, coupled with JdbcTemplate's update method.
The code is as follows:
In My Entity:
#Entity
#Table(name = "MyTable", indexes = { #Index(name = "my_index", columnList = "mySequencedValue") })
public class MyEntity {
//...
#Column(columnDefinition = "integer unsigned", nullable = false, updatable = false, insertable = false)
private Long mySequencedValue;
//...
}
In a PostConstructComponent class:
#Component
public class PostConstructComponent {
#Autowired
private JdbcTemplate jdbcTemplate;
#PostConstruct
public void makeMyEntityMySequencedValueAutoIncremental() {
jdbcTemplate.update("alter table MyTable modify mySequencedValue int unsigned auto_increment");
}
}
I was struggling with this today, was able to solve using this
#Generated(GenerationTime.INSERT)
#Column(name = "internal_id", columnDefinition = "serial", updatable = false)
private int internalId;
#Column(name = "<column name>", columnDefinition = "serial")
Works for mySQL
I've made a separate entity table for generating id and used it in to set this non-primay key id in the service that holds that id.
Entity:
import lombok.Data;
#Entity
#Data
public class GeneralSeqGenerator {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "my_gen")
#SequenceGenerator(name = "my_gen", sequenceName= "my_seq", allocationSize = 1, initialValue = 100000)
private long seqNumber;
}
Repository:
public interface GeneralSeqGeneratorRepository extends JpaRepository<GeneralSeqGenerator, Long>{
}
Implementation of the service that holds non-primary id:
...
public void saveNewEntity(...) {
...
newEntity.setNonPrimaryId(generalSeqGeneratorRepository.save(new GeneralSeqGenerator()).getSeqNumber());
...
}
...
I've been in a situation like you (JPA/Hibernate sequence for non #Id field) and I ended up creating a trigger in my db schema that add a unique sequence number on insert. I just never got it to work with JPA/Hibernate
After spending hours, this neatly helped me to solve my problem:
For Oracle 12c:
ID NUMBER GENERATED as IDENTITY
For H2:
ID BIGINT GENERATED as auto_increment
Also make:
#Column(insertable = false)

H2 and Hibernate (JPA) -> GenerationType.IDENTITY after persisting [duplicate]

Is it possible to use a DB sequence for some column that is not the identifier/is not part of a composite identifier?
I'm using hibernate as jpa provider, and I have a table that has some columns that are generated values (using a sequence), although they are not part of the identifier.
What I want is to use a sequence to create a new value for an entity, where the column for the sequence is NOT (part of) the primary key:
#Entity
#Table(name = "MyTable")
public class MyEntity {
//...
#Id //... etc
public Long getId() {
return id;
}
//note NO #Id here! but this doesn't work...
#GeneratedValue(strategy = GenerationType.AUTO, generator = "myGen")
#SequenceGenerator(name = "myGen", sequenceName = "MY_SEQUENCE")
#Column(name = "SEQ_VAL", unique = false, nullable = false, insertable = true, updatable = true)
public Long getMySequencedValue(){
return myVal;
}
}
Then when I do this:
em.persist(new MyEntity());
the id will be generated, but the mySequenceVal property will be also generated by my JPA provider.
Just to make things clear: I want Hibernate to generate the value for the mySequencedValue property. I know Hibernate can handle database-generated values, but I don't want to use a trigger or any other thing other than Hibernate itself to generate the value for my property. If Hibernate can generate values for primary keys, why can't it generate for a simple property?
Looking for answers to this problem, I stumbled upon this link
It seems that Hibernate/JPA isn't able to automatically create a value for your non-id-properties. The #GeneratedValue annotation is only used in conjunction with #Id to create auto-numbers.
The #GeneratedValue annotation just tells Hibernate that the database is generating this value itself.
The solution (or work-around) suggested in that forum is to create a separate entity with a generated Id, something like this:
#Entity
public class GeneralSequenceNumber {
#Id
#GeneratedValue(...)
private Long number;
}
#Entity
public class MyEntity {
#Id ..
private Long id;
#OneToOne(...)
private GeneralSequnceNumber myVal;
}
I found that #Column(columnDefinition="serial") works perfect but only for PostgreSQL. For me this was perfect solution, because second entity is "ugly" option.
A call to saveAndFlush on the entity is also necessary, and save won't be enough to populate the value from the DB.
I know this is a very old question, but it's showed firstly upon the results and jpa has changed a lot since the question.
The right way to do it now is with the #Generated annotation. You can define the sequence, set the default in the column to that sequence and then map the column as:
#Generated(GenerationTime.INSERT)
#Column(name = "column_name", insertable = false)
Hibernate definitely supports this. From the docs:
"Generated properties are properties which have their values generated by the database. Typically, Hibernate applications needed to refresh objects which contain any properties for which the database was generating values. Marking properties as generated, however, lets the application delegate this responsibility to Hibernate. Essentially, whenever Hibernate issues an SQL INSERT or UPDATE for an entity which has defined generated properties, it immediately issues a select afterwards to retrieve the generated values."
For properties generated on insert only, your property mapping (.hbm.xml) would look like:
<property name="foo" generated="insert"/>
For properties generated on insert and update your property mapping (.hbm.xml) would look like:
<property name="foo" generated="always"/>
Unfortunately, I don't know JPA, so I don't know if this feature is exposed via JPA (I suspect possibly not)
Alternatively, you should be able to exclude the property from inserts and updates, and then "manually" call session.refresh( obj ); after you have inserted/updated it to load the generated value from the database.
This is how you would exclude the property from being used in insert and update statements:
<property name="foo" update="false" insert="false"/>
Again, I don't know if JPA exposes these Hibernate features, but Hibernate does support them.
I fixed the generation of UUID (or sequences) with Hibernate using #PrePersist annotation:
#PrePersist
public void initializeUUID() {
if (uuid == null) {
uuid = UUID.randomUUID().toString();
}
}
Looks like thread is old, I just wanted to add my solution here(Using AspectJ - AOP in spring).
Solution is to create a custom annotation #InjectSequenceValue as follows.
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
public #interface InjectSequenceValue {
String sequencename();
}
Now you can annotate any field in entity, so that the underlying field (Long/Integer) value will be injected at runtime using the nextvalue of the sequence.
Annotate like this.
//serialNumber will be injected dynamically, with the next value of the serialnum_sequence.
#InjectSequenceValue(sequencename = "serialnum_sequence")
Long serialNumber;
So far we have marked the field we need to inject the sequence value.So we will look how to inject the sequence value to the marked fields, this is done by creating the point cut in AspectJ.
We will trigger the injection just before the save/persist method is being executed.This is done in the below class.
#Aspect
#Configuration
public class AspectDefinition {
#Autowired
JdbcTemplate jdbcTemplate;
//#Before("execution(* org.hibernate.session.save(..))") Use this for Hibernate.(also include session.save())
#Before("execution(* org.springframework.data.repository.CrudRepository.save(..))") //This is for JPA.
public void generateSequence(JoinPoint joinPoint){
Object [] aragumentList=joinPoint.getArgs(); //Getting all arguments of the save
for (Object arg :aragumentList ) {
if (arg.getClass().isAnnotationPresent(Entity.class)){ // getting the Entity class
Field[] fields = arg.getClass().getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(InjectSequenceValue.class)) { //getting annotated fields
field.setAccessible(true);
try {
if (field.get(arg) == null){ // Setting the next value
String sequenceName=field.getAnnotation(InjectSequenceValue.class).sequencename();
long nextval=getNextValue(sequenceName);
System.out.println("Next value :"+nextval); //TODO remove sout.
field.set(arg, nextval);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
}
/**
* This method fetches the next value from sequence
* #param sequence
* #return
*/
public long getNextValue(String sequence){
long sequenceNextVal=0L;
SqlRowSet sqlRowSet= jdbcTemplate.queryForRowSet("SELECT "+sequence+".NEXTVAL as value FROM DUAL");
while (sqlRowSet.next()){
sequenceNextVal=sqlRowSet.getLong("value");
}
return sequenceNextVal;
}
}
Now you can annotate any Entity as below.
#Entity
#Table(name = "T_USER")
public class UserEntity {
#Id
#SequenceGenerator(sequenceName = "userid_sequence",name = "this_seq")
#GeneratedValue(strategy = GenerationType.SEQUENCE,generator = "this_seq")
Long id;
String userName;
String password;
#InjectSequenceValue(sequencename = "serialnum_sequence") // this will be injected at the time of saving.
Long serialNumber;
String name;
}
As a followup here's how I got it to work:
#Override public Long getNextExternalId() {
BigDecimal seq =
(BigDecimal)((List)em.createNativeQuery("select col_msd_external_id_seq.nextval from dual").getResultList()).get(0);
return seq.longValue();
}
If you are using postgresql
And i'm using in spring boot 1.5.6
#Column(columnDefinition = "serial")
#Generated(GenerationTime.INSERT)
private Integer orderID;
Although this is an old thread I want to share my solution and hopefully get some feedback on this. Be warned that I only tested this solution with my local database in some JUnit testcase. So this is not a productive feature so far.
I solved that issue for my by introducing a custom annotation called Sequence with no property. It's just a marker for fields that should be assigned a value from an incremented sequence.
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
public #interface Sequence
{
}
Using this annotation i marked my entities.
public class Area extends BaseEntity implements ClientAware, IssuerAware
{
#Column(name = "areaNumber", updatable = false)
#Sequence
private Integer areaNumber;
....
}
To keep things database independent I introduced an entity called SequenceNumber which holds the sequence current value and the increment size. I chose the className as unique key so each entity class wil get its own sequence.
#Entity
#Table(name = "SequenceNumber", uniqueConstraints = { #UniqueConstraint(columnNames = { "className" }) })
public class SequenceNumber
{
#Id
#Column(name = "className", updatable = false)
private String className;
#Column(name = "nextValue")
private Integer nextValue = 1;
#Column(name = "incrementValue")
private Integer incrementValue = 10;
... some getters and setters ....
}
The last step and the most difficult is a PreInsertListener that handles the sequence number assignment. Note that I used spring as bean container.
#Component
public class SequenceListener implements PreInsertEventListener
{
private static final long serialVersionUID = 7946581162328559098L;
private final static Logger log = Logger.getLogger(SequenceListener.class);
#Autowired
private SessionFactoryImplementor sessionFactoryImpl;
private final Map<String, CacheEntry> cache = new HashMap<>();
#PostConstruct
public void selfRegister()
{
// As you might expect, an EventListenerRegistry is the place with which event listeners are registered
// It is a service so we look it up using the service registry
final EventListenerRegistry eventListenerRegistry = sessionFactoryImpl.getServiceRegistry().getService(EventListenerRegistry.class);
// add the listener to the end of the listener chain
eventListenerRegistry.appendListeners(EventType.PRE_INSERT, this);
}
#Override
public boolean onPreInsert(PreInsertEvent p_event)
{
updateSequenceValue(p_event.getEntity(), p_event.getState(), p_event.getPersister().getPropertyNames());
return false;
}
private void updateSequenceValue(Object p_entity, Object[] p_state, String[] p_propertyNames)
{
try
{
List<Field> fields = ReflectUtil.getFields(p_entity.getClass(), null, Sequence.class);
if (!fields.isEmpty())
{
if (log.isDebugEnabled())
{
log.debug("Intercepted custom sequence entity.");
}
for (Field field : fields)
{
Integer value = getSequenceNumber(p_entity.getClass().getName());
field.setAccessible(true);
field.set(p_entity, value);
setPropertyState(p_state, p_propertyNames, field.getName(), value);
if (log.isDebugEnabled())
{
LogMF.debug(log, "Set {0} property to {1}.", new Object[] { field, value });
}
}
}
}
catch (Exception e)
{
log.error("Failed to set sequence property.", e);
}
}
private Integer getSequenceNumber(String p_className)
{
synchronized (cache)
{
CacheEntry current = cache.get(p_className);
// not in cache yet => load from database
if ((current == null) || current.isEmpty())
{
boolean insert = false;
StatelessSession session = sessionFactoryImpl.openStatelessSession();
session.beginTransaction();
SequenceNumber sequenceNumber = (SequenceNumber) session.get(SequenceNumber.class, p_className);
// not in database yet => create new sequence
if (sequenceNumber == null)
{
sequenceNumber = new SequenceNumber();
sequenceNumber.setClassName(p_className);
insert = true;
}
current = new CacheEntry(sequenceNumber.getNextValue() + sequenceNumber.getIncrementValue(), sequenceNumber.getNextValue());
cache.put(p_className, current);
sequenceNumber.setNextValue(sequenceNumber.getNextValue() + sequenceNumber.getIncrementValue());
if (insert)
{
session.insert(sequenceNumber);
}
else
{
session.update(sequenceNumber);
}
session.getTransaction().commit();
session.close();
}
return current.next();
}
}
private void setPropertyState(Object[] propertyStates, String[] propertyNames, String propertyName, Object propertyState)
{
for (int i = 0; i < propertyNames.length; i++)
{
if (propertyName.equals(propertyNames[i]))
{
propertyStates[i] = propertyState;
return;
}
}
}
private static class CacheEntry
{
private int current;
private final int limit;
public CacheEntry(final int p_limit, final int p_current)
{
current = p_current;
limit = p_limit;
}
public Integer next()
{
return current++;
}
public boolean isEmpty()
{
return current >= limit;
}
}
}
As you can see from the above code the listener used one SequenceNumber instance per entity class and reserves a couple of sequence numbers defined by the incrementValue of the SequenceNumber entity. If it runs out of sequence numbers it loads the SequenceNumber entity for the target class and reserves incrementValue values for the next calls. This way I do not need to query the database each time a sequence value is needed.
Note the StatelessSession that is being opened for reserving the next set of sequence numbers. You cannot use the same session the target entity is currently persisted since this would lead to a ConcurrentModificationException in the EntityPersister.
Hope this helps someone.
I run in the same situation like you and I also didn't find any serious answers if it is basically possible to generate non-id propertys with JPA or not.
My solution is to call the sequence with a native JPA query to set the property by hand before persisiting it.
This is not satisfying but it works as a workaround for the moment.
Mario
I've found this specific note in session 9.1.9 GeneratedValue Annotation from JPA specification:
"[43] Portable applications should not use the GeneratedValue annotation on other persistent fields or properties."
So, I presume that it is not possible to auto generate value for non primary key values at least using simply JPA.
You can do exactly what you are asking.
I've found it is possible to adapt Hibernate's IdentifierGenerator implementations by registering them with an Integrator. With this you should be able to use any id sequence generator provided by Hibernate to generate sequences for non-id fields (presumably the non-sequential id generators would work as well).
There are quite a few options for generating ids this way. Check out some of the implementations of IdentifierGenerator, specifically SequenceStyleGenerator and TableGenerator. If you have configured generators using the #GenericGenerator annotation, then the parameters for these classes may be familiar to you. This would also have the advantage of using Hibernate to generate the SQL.
Here is how I got it working:
import org.hibernate.Session;
import org.hibernate.boot.Metadata;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.id.enhanced.TableGenerator;
import org.hibernate.integrator.spi.Integrator;
import org.hibernate.internal.SessionImpl;
import org.hibernate.service.spi.SessionFactoryServiceRegistry;
import org.hibernate.tuple.ValueGenerator;
import org.hibernate.type.LongType;
import java.util.Properties;
public class SequenceIntegrator implements Integrator, ValueGenerator<Long> {
public static final String TABLE_NAME = "SEQUENCE_TABLE";
public static final String VALUE_COLUMN_NAME = "NEXT_VAL";
public static final String SEGMENT_COLUMN_NAME = "SEQUENCE_NAME";
private static SessionFactoryServiceRegistry serviceRegistry;
private static Metadata metadata;
private static IdentifierGenerator defaultGenerator;
#Override
public void integrate(Metadata metadata, SessionFactoryImplementor sessionFactoryImplementor, SessionFactoryServiceRegistry sessionFactoryServiceRegistry) {
//assigning metadata and registry to fields for use in a below example
SequenceIntegrator.metadata = metadata;
SequenceIntegrator.serviceRegistry = sessionFactoryServiceRegistry;
SequenceIntegrator.defaultGenerator = getTableGenerator(metadata, sessionFactoryServiceRegistry, "DEFAULT");
}
private TableGenerator getTableGenerator(Metadata metadata, SessionFactoryServiceRegistry sessionFactoryServiceRegistry, String segmentValue) {
TableGenerator generator = new TableGenerator();
Properties properties = new Properties();
properties.setProperty("table_name", TABLE_NAME);
properties.setProperty("value_column_name", VALUE_COLUMN_NAME);
properties.setProperty("segment_column_name", SEGMENT_COLUMN_NAME);
properties.setProperty("segment_value", segmentValue);
//any type should work if the generator supports it
generator.configure(LongType.INSTANCE, properties, sessionFactoryServiceRegistry);
//this should create the table if ddl auto update is enabled and if this function is called inside of the integrate method
generator.registerExportables(metadata.getDatabase());
return generator;
}
#Override
public Long generateValue(Session session, Object o) {
// registering additional generators with getTableGenerator will work here. inserting new sequences can be done dynamically
// example:
// TableGenerator classSpecificGenerator = getTableGenerator(metadata, serviceRegistry, o.getClass().getName());
// return (Long) classSpecificGenerator.generate((SessionImpl)session, o);
return (Long) defaultGenerator.generate((SessionImpl)session, o);
}
#Override
public void disintegrate(SessionFactoryImplementor sessionFactoryImplementor, SessionFactoryServiceRegistry sessionFactoryServiceRegistry) {
}
}
You would need to register this class in the META-INF/services directory. Here is what the Hibernate documentation has to say about registering an Integrator:
For the integrator to be automatically used when Hibernate starts up, you will need to add a META-INF/services/org.hibernate.integrator.spi.Integrator file to your jar. The file should contain the fully qualified name of the class implementing the interface.
Because this class implements the ValueGenerator class, it can be used with the #GeneratorType annotation to automatically generate the sequential values. Here is how your class might be configured:
#Entity
#Table(name = "MyTable")
public class MyEntity {
//...
#Id //... etc
public Long getId() {
return id;
}
#GeneratorType(type = SequenceIntegrator.class, when = GenerationTime.INSERT)
#Column(name = "SEQ_VAL", unique = false, nullable = false, insertable = true, updatable = true)
public Long getMySequencedValue(){
return myVal;
}
}
I want to provide an alternative next to #Morten Berg's accepted solution, which worked better for me.
This approach allows to define the field with the actually desired Number type - Long in my use case - instead of GeneralSequenceNumber. This can be useful, e.g. for JSON (de-)serialization.
The downside is that it requires a little more database overhead.
First, we need an ActualEntity in which we want to auto-increment generated of type Long:
// ...
#Entity
public class ActualEntity {
#Id
// ...
Long id;
#Column(unique = true, updatable = false, nullable = false)
Long generated;
// ...
}
Next, we need a helper entity Generated. I placed it package-private next to ActualEntity, to keep it an implementation detail of the package:
#Entity
class Generated {
#Id
#GeneratedValue(strategy = SEQUENCE, generator = "seq")
#SequenceGenerator(name = "seq", initialValue = 1, allocationSize = 1)
Long id;
}
Finally, we need a place to hook in right before we save the ActualEntity. There, we create and persist aGenerated instance. This then provides a database-sequence generated id of type Long. We make use of this value by writing it to ActualEntity.generated.
In my use case, I implemented this using a Spring Data REST #RepositoryEventHandler, which get's called right before the ActualEntity get's persisted. It should demonstrate the principle:
#Component
#RepositoryEventHandler
public class ActualEntityHandler {
#Autowired
EntityManager entityManager;
#Transactional
#HandleBeforeCreate
public void generate(ActualEntity entity) {
Generated generated = new Generated();
entityManager.persist(generated);
entity.setGlobalId(generated.getId());
entityManager.remove(generated);
}
}
I didn't test it in a real-life application, so please enjoy with care.
"I don't want to use a trigger or any other thing other than Hibernate itself to generate the value for my property"
In that case, how about creating an implementation of UserType which generates the required value, and configuring the metadata to use that UserType for persistence of the mySequenceVal property?
This is not the same as using a sequence. When using a sequence, you are not inserting or updating anything. You are simply retrieving the next sequence value. It looks like hibernate does not support it.
If you have a column with UNIQUEIDENTIFIER type and default generation needed on insert but column is not PK
#Generated(GenerationTime.INSERT)
#Column(nullable = false , columnDefinition="UNIQUEIDENTIFIER")
private String uuidValue;
In db you will have
CREATE TABLE operation.Table1
(
Id INT IDENTITY (1,1) NOT NULL,
UuidValue UNIQUEIDENTIFIER DEFAULT NEWID() NOT NULL)
In this case you will not define generator for a value which you need (It will be automatically thanks to columnDefinition="UNIQUEIDENTIFIER"). The same you can try for other column types
I have found a workaround for this on MySql databases using #PostConstruct and JdbcTemplate in a Spring application. It may be doable with other databases but the use case that I will present is based on my experience with MySql, as it uses auto_increment.
First, I had tried defining a column as auto_increment using the ColumnDefinition property of the #Column annotation, but it was not working as the column needed to be an key in order to be auto incremental, but apparently the column wouldn't be defined as an index until after it was defined, causing a deadlock.
Here is where I came with the idea of creating the column without the auto_increment definition, and adding it after the database was created. This is possible using the #PostConstruct annotation, which causes a method to be invoked right after the application has initialized the beans, coupled with JdbcTemplate's update method.
The code is as follows:
In My Entity:
#Entity
#Table(name = "MyTable", indexes = { #Index(name = "my_index", columnList = "mySequencedValue") })
public class MyEntity {
//...
#Column(columnDefinition = "integer unsigned", nullable = false, updatable = false, insertable = false)
private Long mySequencedValue;
//...
}
In a PostConstructComponent class:
#Component
public class PostConstructComponent {
#Autowired
private JdbcTemplate jdbcTemplate;
#PostConstruct
public void makeMyEntityMySequencedValueAutoIncremental() {
jdbcTemplate.update("alter table MyTable modify mySequencedValue int unsigned auto_increment");
}
}
I was struggling with this today, was able to solve using this
#Generated(GenerationTime.INSERT)
#Column(name = "internal_id", columnDefinition = "serial", updatable = false)
private int internalId;
#Column(name = "<column name>", columnDefinition = "serial")
Works for mySQL
I've made a separate entity table for generating id and used it in to set this non-primay key id in the service that holds that id.
Entity:
import lombok.Data;
#Entity
#Data
public class GeneralSeqGenerator {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "my_gen")
#SequenceGenerator(name = "my_gen", sequenceName= "my_seq", allocationSize = 1, initialValue = 100000)
private long seqNumber;
}
Repository:
public interface GeneralSeqGeneratorRepository extends JpaRepository<GeneralSeqGenerator, Long>{
}
Implementation of the service that holds non-primary id:
...
public void saveNewEntity(...) {
...
newEntity.setNonPrimaryId(generalSeqGeneratorRepository.save(new GeneralSeqGenerator()).getSeqNumber());
...
}
...
I've been in a situation like you (JPA/Hibernate sequence for non #Id field) and I ended up creating a trigger in my db schema that add a unique sequence number on insert. I just never got it to work with JPA/Hibernate
After spending hours, this neatly helped me to solve my problem:
For Oracle 12c:
ID NUMBER GENERATED as IDENTITY
For H2:
ID BIGINT GENERATED as auto_increment
Also make:
#Column(insertable = false)

Hibernate/persistence and singleton pattern

I have a singleton that is to be persisted in database. Other persisted entities should have reference to this singleton. How can it be achieved with Hibernate?
I ended with something not-yet-working like this, faking the single ID of the singleton:
#Entity
#Subselect("select '1' as ID")
class Singleton {
#Id
#Column(name="ID")
private Long getId() { return 1l; }
private void setId(Long id) { }
}
#Entity
#Table(name="ENT")
class MyEnt {
// TODO: how to annotate so that table ENT doesn't need foreign key column
Singleton s;
}
The problem is that I don't want to have a column with foreign key in entities referencing the Singleton - because the singleton is only one and we don't need its ID...
Maybe I am thinking in a wrong way? Maybe it is wrong architecture issue? Did you solve similar issue?
I ended with this:
#Entity
#Subselect("select '1' as ID, * from TABLE")
class Singleton {
#Id
#Column(name="ID")
private Long getId() { return 1l; }
private void setId(Long id) { }
// ... other useful fields persisted in TABLE
}
#Entity
#Table(name="ENT")
class MyEnt implements Lifecycle {
Singleton s;
void onLoad(Session sess, Serializable id) {
this.s = sess.get(Singleton.class, 1l);
}
// etc...
}
If Singleton is only one object, why do you want to map it in other entities?
You may want to load Singleton once and put it in application context. In that case, you may reuse it whenever you need it.

JPA mapped model returns null elements with Composite-Keys

I have build my data model using JPA and am using Hibernate's EntityManager 3 to access the data. I used HSQLDB for testing (junit). I am using this configuration for other classes and have had no problems.
However, the latest batch of tables use a composite-key as the primary-key and I am not able to retrieve the populated row from the database when it is implemented. I don't get an error, the query simply returns null objects.
For example if I query (using jsql) "FROM Order o" to return a list of all orders in the table, my list.size() has the proper number of elements (2), but the elements are null.
I am hoping that someone with a sharper eye than I can discern what I am doing wrong. Thanks in advance!
The (simplified) tables are defined as:
CREATE TABLE member (
member_id INTEGER NOT NULL IDENTITY PRIMARY KEY);
CREATE TABLE orders (
orders_id INTEGER NOT NULL,
member_id INTEGER NOT NULL,
PRIMARY KEY(orders_id, member_id));
ALTER TABLE orders
ADD CONSTRAINT fk_orders_member
FOREIGN KEY (member_id) REFERENCES member(member_id);
The entity POJOs are defined by:
#Entity
public class Member extends Person implements Model<Integer>{
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="MEMBER_ID", nullable=false)
private Integer memberId;
#OneToMany(fetch=FetchType.LAZY, mappedBy="member", cascade=CascadeType.ALL)
private Set<Order> orderList;
}
#Entity
#Table(name="ORDERS")
#IdClass(OrderPK.class)
public class Order extends GeneralTableInformation implements Model<Integer>{
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="ORDERS_ID", nullable=false)
private Integer orderId;
#Id
#Column(name="MEMBER_ID", nullable=false)
private Integer memberId;
#ManyToOne(optional=false, fetch=FetchType.LAZY)
#JoinColumn(name="MEMBER_ID", nullable=false)
private Member member;
#OneToMany(mappedBy="order", fetch=FetchType.LAZY)
private Set<Note> noteList;
}
OrderPK defines a default constructor and 2 properties (orderId, memberId) along with their get/set methods.
public class OrderPK implements Serializable {
private static final long serialVersionUID = 1L;
private Integer orderId;
private Integer memberId;
public OrderPK() {}
public OrderPK(Integer orderId, Integer memberId) {
this.orderId = orderId;
this.memberId = memberId;
}
/**Getters/Setters**/
#Override
public int hashCode() {
return orderId.hashCode() + memberId.hashCode();
}
#Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (!(obj instanceof OrderPK))
return false;
OrderPK other = (OrderPK) obj;
if (memberId == null) {
if (other.memberId != null) return false;
} else if (!memberId.equals(other.memberId))
return false;
if (orderId == null) {
if (other.orderId != null) return false;
} else if (!orderId.equals(other.orderId))
return false;
return true;
}
}
(sorry for the length)
the entityManager is instantiated in an abstract class which is then extended by my other DAOs
protected EntityManager em;
#PersistenceContext
public void setEntityManager(EntityManager em) {
this.em = em;
}
and is configured by a spring context configuration file
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
p:dataSource-ref="dataSource"
p:jpaVendorAdapter-ref="jpaAdapter">
<property name="loadTimeWeaver">
<bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" />
</property>
<property name="persistenceXmlLocation" value="classpath:META-INF/persistence.xml" />
</bean>
My test class
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
public class OrderDaoTest {
#Autowired
protected OrderDao dao = null;
#Test
public void findAllOrdersTest() {
List<Order> ol = dao.findAll();
assertNotNull(ol); //pass
assertEquals(2, ol.size(); //pass
for (Order o : ol) {
assertNotNull(o); //fail
...
}
}
}
When I strip away the composite-key from the Order class I am able to retrieve data, I am not sure what I am doing incorrectly with my mapping or configuration. Any help is greatly appreciated.
After struggling with this problem for awhile longer I learned that I was configuring my Id properties in the wrong class.
Originally I was configuring orderId and memberId in the Order class
#Entity
#Table(name="ORDERS")
#IdClass(OrderPK.class)
public class Order extends GeneralTableInformation implements Model<Integer>{
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="ORDERS_ID", nullable=false)
private Integer orderId;
#Id
#Column(name="MEMBER_ID", nullable=false)
private Integer memberId;
However, I learned that if you are using an IdClass OR EmbeddedId that you must make the appropriate field annotations for your Id columns in the ID Class.
#Entity
#Table(name="ORDERS")
#IdClass(OrderPK.class)
public class Order extends GeneralTableInformation implements Model<Integer>{
#Id
private Integer orderId;
#Id
private Integer memberId;
}
public class OrderPK implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name="ORDERS_ID", nullable=false)
private Integer orderId;
#Column(name="MEMBER_ID", nullable=false)
private Integer memberId;
}
With this change I was able to return the expected results with my test.
I had same kind of problem. I had to create backend on existing database, which had some tables without primary keys and with nullable columns (I know!!). This means that Hibernate entities for these kind of tables would have composite primary key consisting of all of the table columns.
When I queried the table rows through JpaRepository's findAll method, I would get a list of results where some of the results would be null values while others would be ok.
I finally found out that if some field of the composite key is null, it would render whole row as null result.
OrderPK defines a default constructor and 2 properties (orderId, memberId) along with their get/set methods.
I'm not sure this explains everything but... that's not enough. From the JPA 1.0 specification:
2.1.4 Primary Keys and Entity Identity
...
The following rules apply for composite primary keys.
The primary key class must be public and must have a public no-arg constructor.
If property-based access is used, the properties of the primary key class must be public or protected.
The primary key class must be serializable.
The primary key class must define equals and hashCode methods. The semantics of value equality for these methods must be consistent with the database equality for the database types to which the key is mapped.
A composite primary key must either be represented and mapped as an embeddable class (see Section 9.1.14, "EmbeddedId Annotation") or must be represented and mapped to multiple fields or properties of the entity class (see Section 9.1.15, "IdClass Annotation").
If the composite primary key class is mapped to multiple fields or properties of the entity class, the names of primary key fields or properties in the primary key class and those of the entity class must correspond and their types must be the same.
Could you please fix your OrderPK class to comply with the specification and try again?
As a side note, I wonder why you are using the loadTimeWeaver in your Spring configuration (since you're using Hibernate).
Update: I can't reproduce the problem... works as expected for me.

Hibernate JPA Sequence (non-Id)

Is it possible to use a DB sequence for some column that is not the identifier/is not part of a composite identifier?
I'm using hibernate as jpa provider, and I have a table that has some columns that are generated values (using a sequence), although they are not part of the identifier.
What I want is to use a sequence to create a new value for an entity, where the column for the sequence is NOT (part of) the primary key:
#Entity
#Table(name = "MyTable")
public class MyEntity {
//...
#Id //... etc
public Long getId() {
return id;
}
//note NO #Id here! but this doesn't work...
#GeneratedValue(strategy = GenerationType.AUTO, generator = "myGen")
#SequenceGenerator(name = "myGen", sequenceName = "MY_SEQUENCE")
#Column(name = "SEQ_VAL", unique = false, nullable = false, insertable = true, updatable = true)
public Long getMySequencedValue(){
return myVal;
}
}
Then when I do this:
em.persist(new MyEntity());
the id will be generated, but the mySequenceVal property will be also generated by my JPA provider.
Just to make things clear: I want Hibernate to generate the value for the mySequencedValue property. I know Hibernate can handle database-generated values, but I don't want to use a trigger or any other thing other than Hibernate itself to generate the value for my property. If Hibernate can generate values for primary keys, why can't it generate for a simple property?
Looking for answers to this problem, I stumbled upon this link
It seems that Hibernate/JPA isn't able to automatically create a value for your non-id-properties. The #GeneratedValue annotation is only used in conjunction with #Id to create auto-numbers.
The #GeneratedValue annotation just tells Hibernate that the database is generating this value itself.
The solution (or work-around) suggested in that forum is to create a separate entity with a generated Id, something like this:
#Entity
public class GeneralSequenceNumber {
#Id
#GeneratedValue(...)
private Long number;
}
#Entity
public class MyEntity {
#Id ..
private Long id;
#OneToOne(...)
private GeneralSequnceNumber myVal;
}
I found that #Column(columnDefinition="serial") works perfect but only for PostgreSQL. For me this was perfect solution, because second entity is "ugly" option.
A call to saveAndFlush on the entity is also necessary, and save won't be enough to populate the value from the DB.
I know this is a very old question, but it's showed firstly upon the results and jpa has changed a lot since the question.
The right way to do it now is with the #Generated annotation. You can define the sequence, set the default in the column to that sequence and then map the column as:
#Generated(GenerationTime.INSERT)
#Column(name = "column_name", insertable = false)
Hibernate definitely supports this. From the docs:
"Generated properties are properties which have their values generated by the database. Typically, Hibernate applications needed to refresh objects which contain any properties for which the database was generating values. Marking properties as generated, however, lets the application delegate this responsibility to Hibernate. Essentially, whenever Hibernate issues an SQL INSERT or UPDATE for an entity which has defined generated properties, it immediately issues a select afterwards to retrieve the generated values."
For properties generated on insert only, your property mapping (.hbm.xml) would look like:
<property name="foo" generated="insert"/>
For properties generated on insert and update your property mapping (.hbm.xml) would look like:
<property name="foo" generated="always"/>
Unfortunately, I don't know JPA, so I don't know if this feature is exposed via JPA (I suspect possibly not)
Alternatively, you should be able to exclude the property from inserts and updates, and then "manually" call session.refresh( obj ); after you have inserted/updated it to load the generated value from the database.
This is how you would exclude the property from being used in insert and update statements:
<property name="foo" update="false" insert="false"/>
Again, I don't know if JPA exposes these Hibernate features, but Hibernate does support them.
I fixed the generation of UUID (or sequences) with Hibernate using #PrePersist annotation:
#PrePersist
public void initializeUUID() {
if (uuid == null) {
uuid = UUID.randomUUID().toString();
}
}
Looks like thread is old, I just wanted to add my solution here(Using AspectJ - AOP in spring).
Solution is to create a custom annotation #InjectSequenceValue as follows.
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
public #interface InjectSequenceValue {
String sequencename();
}
Now you can annotate any field in entity, so that the underlying field (Long/Integer) value will be injected at runtime using the nextvalue of the sequence.
Annotate like this.
//serialNumber will be injected dynamically, with the next value of the serialnum_sequence.
#InjectSequenceValue(sequencename = "serialnum_sequence")
Long serialNumber;
So far we have marked the field we need to inject the sequence value.So we will look how to inject the sequence value to the marked fields, this is done by creating the point cut in AspectJ.
We will trigger the injection just before the save/persist method is being executed.This is done in the below class.
#Aspect
#Configuration
public class AspectDefinition {
#Autowired
JdbcTemplate jdbcTemplate;
//#Before("execution(* org.hibernate.session.save(..))") Use this for Hibernate.(also include session.save())
#Before("execution(* org.springframework.data.repository.CrudRepository.save(..))") //This is for JPA.
public void generateSequence(JoinPoint joinPoint){
Object [] aragumentList=joinPoint.getArgs(); //Getting all arguments of the save
for (Object arg :aragumentList ) {
if (arg.getClass().isAnnotationPresent(Entity.class)){ // getting the Entity class
Field[] fields = arg.getClass().getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(InjectSequenceValue.class)) { //getting annotated fields
field.setAccessible(true);
try {
if (field.get(arg) == null){ // Setting the next value
String sequenceName=field.getAnnotation(InjectSequenceValue.class).sequencename();
long nextval=getNextValue(sequenceName);
System.out.println("Next value :"+nextval); //TODO remove sout.
field.set(arg, nextval);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
}
/**
* This method fetches the next value from sequence
* #param sequence
* #return
*/
public long getNextValue(String sequence){
long sequenceNextVal=0L;
SqlRowSet sqlRowSet= jdbcTemplate.queryForRowSet("SELECT "+sequence+".NEXTVAL as value FROM DUAL");
while (sqlRowSet.next()){
sequenceNextVal=sqlRowSet.getLong("value");
}
return sequenceNextVal;
}
}
Now you can annotate any Entity as below.
#Entity
#Table(name = "T_USER")
public class UserEntity {
#Id
#SequenceGenerator(sequenceName = "userid_sequence",name = "this_seq")
#GeneratedValue(strategy = GenerationType.SEQUENCE,generator = "this_seq")
Long id;
String userName;
String password;
#InjectSequenceValue(sequencename = "serialnum_sequence") // this will be injected at the time of saving.
Long serialNumber;
String name;
}
As a followup here's how I got it to work:
#Override public Long getNextExternalId() {
BigDecimal seq =
(BigDecimal)((List)em.createNativeQuery("select col_msd_external_id_seq.nextval from dual").getResultList()).get(0);
return seq.longValue();
}
Although this is an old thread I want to share my solution and hopefully get some feedback on this. Be warned that I only tested this solution with my local database in some JUnit testcase. So this is not a productive feature so far.
I solved that issue for my by introducing a custom annotation called Sequence with no property. It's just a marker for fields that should be assigned a value from an incremented sequence.
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
public #interface Sequence
{
}
Using this annotation i marked my entities.
public class Area extends BaseEntity implements ClientAware, IssuerAware
{
#Column(name = "areaNumber", updatable = false)
#Sequence
private Integer areaNumber;
....
}
To keep things database independent I introduced an entity called SequenceNumber which holds the sequence current value and the increment size. I chose the className as unique key so each entity class wil get its own sequence.
#Entity
#Table(name = "SequenceNumber", uniqueConstraints = { #UniqueConstraint(columnNames = { "className" }) })
public class SequenceNumber
{
#Id
#Column(name = "className", updatable = false)
private String className;
#Column(name = "nextValue")
private Integer nextValue = 1;
#Column(name = "incrementValue")
private Integer incrementValue = 10;
... some getters and setters ....
}
The last step and the most difficult is a PreInsertListener that handles the sequence number assignment. Note that I used spring as bean container.
#Component
public class SequenceListener implements PreInsertEventListener
{
private static final long serialVersionUID = 7946581162328559098L;
private final static Logger log = Logger.getLogger(SequenceListener.class);
#Autowired
private SessionFactoryImplementor sessionFactoryImpl;
private final Map<String, CacheEntry> cache = new HashMap<>();
#PostConstruct
public void selfRegister()
{
// As you might expect, an EventListenerRegistry is the place with which event listeners are registered
// It is a service so we look it up using the service registry
final EventListenerRegistry eventListenerRegistry = sessionFactoryImpl.getServiceRegistry().getService(EventListenerRegistry.class);
// add the listener to the end of the listener chain
eventListenerRegistry.appendListeners(EventType.PRE_INSERT, this);
}
#Override
public boolean onPreInsert(PreInsertEvent p_event)
{
updateSequenceValue(p_event.getEntity(), p_event.getState(), p_event.getPersister().getPropertyNames());
return false;
}
private void updateSequenceValue(Object p_entity, Object[] p_state, String[] p_propertyNames)
{
try
{
List<Field> fields = ReflectUtil.getFields(p_entity.getClass(), null, Sequence.class);
if (!fields.isEmpty())
{
if (log.isDebugEnabled())
{
log.debug("Intercepted custom sequence entity.");
}
for (Field field : fields)
{
Integer value = getSequenceNumber(p_entity.getClass().getName());
field.setAccessible(true);
field.set(p_entity, value);
setPropertyState(p_state, p_propertyNames, field.getName(), value);
if (log.isDebugEnabled())
{
LogMF.debug(log, "Set {0} property to {1}.", new Object[] { field, value });
}
}
}
}
catch (Exception e)
{
log.error("Failed to set sequence property.", e);
}
}
private Integer getSequenceNumber(String p_className)
{
synchronized (cache)
{
CacheEntry current = cache.get(p_className);
// not in cache yet => load from database
if ((current == null) || current.isEmpty())
{
boolean insert = false;
StatelessSession session = sessionFactoryImpl.openStatelessSession();
session.beginTransaction();
SequenceNumber sequenceNumber = (SequenceNumber) session.get(SequenceNumber.class, p_className);
// not in database yet => create new sequence
if (sequenceNumber == null)
{
sequenceNumber = new SequenceNumber();
sequenceNumber.setClassName(p_className);
insert = true;
}
current = new CacheEntry(sequenceNumber.getNextValue() + sequenceNumber.getIncrementValue(), sequenceNumber.getNextValue());
cache.put(p_className, current);
sequenceNumber.setNextValue(sequenceNumber.getNextValue() + sequenceNumber.getIncrementValue());
if (insert)
{
session.insert(sequenceNumber);
}
else
{
session.update(sequenceNumber);
}
session.getTransaction().commit();
session.close();
}
return current.next();
}
}
private void setPropertyState(Object[] propertyStates, String[] propertyNames, String propertyName, Object propertyState)
{
for (int i = 0; i < propertyNames.length; i++)
{
if (propertyName.equals(propertyNames[i]))
{
propertyStates[i] = propertyState;
return;
}
}
}
private static class CacheEntry
{
private int current;
private final int limit;
public CacheEntry(final int p_limit, final int p_current)
{
current = p_current;
limit = p_limit;
}
public Integer next()
{
return current++;
}
public boolean isEmpty()
{
return current >= limit;
}
}
}
As you can see from the above code the listener used one SequenceNumber instance per entity class and reserves a couple of sequence numbers defined by the incrementValue of the SequenceNumber entity. If it runs out of sequence numbers it loads the SequenceNumber entity for the target class and reserves incrementValue values for the next calls. This way I do not need to query the database each time a sequence value is needed.
Note the StatelessSession that is being opened for reserving the next set of sequence numbers. You cannot use the same session the target entity is currently persisted since this would lead to a ConcurrentModificationException in the EntityPersister.
Hope this helps someone.
If you are using postgresql
And i'm using in spring boot 1.5.6
#Column(columnDefinition = "serial")
#Generated(GenerationTime.INSERT)
private Integer orderID;
I run in the same situation like you and I also didn't find any serious answers if it is basically possible to generate non-id propertys with JPA or not.
My solution is to call the sequence with a native JPA query to set the property by hand before persisiting it.
This is not satisfying but it works as a workaround for the moment.
Mario
I've found this specific note in session 9.1.9 GeneratedValue Annotation from JPA specification:
"[43] Portable applications should not use the GeneratedValue annotation on other persistent fields or properties."
So, I presume that it is not possible to auto generate value for non primary key values at least using simply JPA.
I want to provide an alternative next to #Morten Berg's accepted solution, which worked better for me.
This approach allows to define the field with the actually desired Number type - Long in my use case - instead of GeneralSequenceNumber. This can be useful, e.g. for JSON (de-)serialization.
The downside is that it requires a little more database overhead.
First, we need an ActualEntity in which we want to auto-increment generated of type Long:
// ...
#Entity
public class ActualEntity {
#Id
// ...
Long id;
#Column(unique = true, updatable = false, nullable = false)
Long generated;
// ...
}
Next, we need a helper entity Generated. I placed it package-private next to ActualEntity, to keep it an implementation detail of the package:
#Entity
class Generated {
#Id
#GeneratedValue(strategy = SEQUENCE, generator = "seq")
#SequenceGenerator(name = "seq", initialValue = 1, allocationSize = 1)
Long id;
}
Finally, we need a place to hook in right before we save the ActualEntity. There, we create and persist aGenerated instance. This then provides a database-sequence generated id of type Long. We make use of this value by writing it to ActualEntity.generated.
In my use case, I implemented this using a Spring Data REST #RepositoryEventHandler, which get's called right before the ActualEntity get's persisted. It should demonstrate the principle:
#Component
#RepositoryEventHandler
public class ActualEntityHandler {
#Autowired
EntityManager entityManager;
#Transactional
#HandleBeforeCreate
public void generate(ActualEntity entity) {
Generated generated = new Generated();
entityManager.persist(generated);
entity.setGlobalId(generated.getId());
entityManager.remove(generated);
}
}
I didn't test it in a real-life application, so please enjoy with care.
You can do exactly what you are asking.
I've found it is possible to adapt Hibernate's IdentifierGenerator implementations by registering them with an Integrator. With this you should be able to use any id sequence generator provided by Hibernate to generate sequences for non-id fields (presumably the non-sequential id generators would work as well).
There are quite a few options for generating ids this way. Check out some of the implementations of IdentifierGenerator, specifically SequenceStyleGenerator and TableGenerator. If you have configured generators using the #GenericGenerator annotation, then the parameters for these classes may be familiar to you. This would also have the advantage of using Hibernate to generate the SQL.
Here is how I got it working:
import org.hibernate.Session;
import org.hibernate.boot.Metadata;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.id.enhanced.TableGenerator;
import org.hibernate.integrator.spi.Integrator;
import org.hibernate.internal.SessionImpl;
import org.hibernate.service.spi.SessionFactoryServiceRegistry;
import org.hibernate.tuple.ValueGenerator;
import org.hibernate.type.LongType;
import java.util.Properties;
public class SequenceIntegrator implements Integrator, ValueGenerator<Long> {
public static final String TABLE_NAME = "SEQUENCE_TABLE";
public static final String VALUE_COLUMN_NAME = "NEXT_VAL";
public static final String SEGMENT_COLUMN_NAME = "SEQUENCE_NAME";
private static SessionFactoryServiceRegistry serviceRegistry;
private static Metadata metadata;
private static IdentifierGenerator defaultGenerator;
#Override
public void integrate(Metadata metadata, SessionFactoryImplementor sessionFactoryImplementor, SessionFactoryServiceRegistry sessionFactoryServiceRegistry) {
//assigning metadata and registry to fields for use in a below example
SequenceIntegrator.metadata = metadata;
SequenceIntegrator.serviceRegistry = sessionFactoryServiceRegistry;
SequenceIntegrator.defaultGenerator = getTableGenerator(metadata, sessionFactoryServiceRegistry, "DEFAULT");
}
private TableGenerator getTableGenerator(Metadata metadata, SessionFactoryServiceRegistry sessionFactoryServiceRegistry, String segmentValue) {
TableGenerator generator = new TableGenerator();
Properties properties = new Properties();
properties.setProperty("table_name", TABLE_NAME);
properties.setProperty("value_column_name", VALUE_COLUMN_NAME);
properties.setProperty("segment_column_name", SEGMENT_COLUMN_NAME);
properties.setProperty("segment_value", segmentValue);
//any type should work if the generator supports it
generator.configure(LongType.INSTANCE, properties, sessionFactoryServiceRegistry);
//this should create the table if ddl auto update is enabled and if this function is called inside of the integrate method
generator.registerExportables(metadata.getDatabase());
return generator;
}
#Override
public Long generateValue(Session session, Object o) {
// registering additional generators with getTableGenerator will work here. inserting new sequences can be done dynamically
// example:
// TableGenerator classSpecificGenerator = getTableGenerator(metadata, serviceRegistry, o.getClass().getName());
// return (Long) classSpecificGenerator.generate((SessionImpl)session, o);
return (Long) defaultGenerator.generate((SessionImpl)session, o);
}
#Override
public void disintegrate(SessionFactoryImplementor sessionFactoryImplementor, SessionFactoryServiceRegistry sessionFactoryServiceRegistry) {
}
}
You would need to register this class in the META-INF/services directory. Here is what the Hibernate documentation has to say about registering an Integrator:
For the integrator to be automatically used when Hibernate starts up, you will need to add a META-INF/services/org.hibernate.integrator.spi.Integrator file to your jar. The file should contain the fully qualified name of the class implementing the interface.
Because this class implements the ValueGenerator class, it can be used with the #GeneratorType annotation to automatically generate the sequential values. Here is how your class might be configured:
#Entity
#Table(name = "MyTable")
public class MyEntity {
//...
#Id //... etc
public Long getId() {
return id;
}
#GeneratorType(type = SequenceIntegrator.class, when = GenerationTime.INSERT)
#Column(name = "SEQ_VAL", unique = false, nullable = false, insertable = true, updatable = true)
public Long getMySequencedValue(){
return myVal;
}
}
"I don't want to use a trigger or any other thing other than Hibernate itself to generate the value for my property"
In that case, how about creating an implementation of UserType which generates the required value, and configuring the metadata to use that UserType for persistence of the mySequenceVal property?
This is not the same as using a sequence. When using a sequence, you are not inserting or updating anything. You are simply retrieving the next sequence value. It looks like hibernate does not support it.
If you have a column with UNIQUEIDENTIFIER type and default generation needed on insert but column is not PK
#Generated(GenerationTime.INSERT)
#Column(nullable = false , columnDefinition="UNIQUEIDENTIFIER")
private String uuidValue;
In db you will have
CREATE TABLE operation.Table1
(
Id INT IDENTITY (1,1) NOT NULL,
UuidValue UNIQUEIDENTIFIER DEFAULT NEWID() NOT NULL)
In this case you will not define generator for a value which you need (It will be automatically thanks to columnDefinition="UNIQUEIDENTIFIER"). The same you can try for other column types
I have found a workaround for this on MySql databases using #PostConstruct and JdbcTemplate in a Spring application. It may be doable with other databases but the use case that I will present is based on my experience with MySql, as it uses auto_increment.
First, I had tried defining a column as auto_increment using the ColumnDefinition property of the #Column annotation, but it was not working as the column needed to be an key in order to be auto incremental, but apparently the column wouldn't be defined as an index until after it was defined, causing a deadlock.
Here is where I came with the idea of creating the column without the auto_increment definition, and adding it after the database was created. This is possible using the #PostConstruct annotation, which causes a method to be invoked right after the application has initialized the beans, coupled with JdbcTemplate's update method.
The code is as follows:
In My Entity:
#Entity
#Table(name = "MyTable", indexes = { #Index(name = "my_index", columnList = "mySequencedValue") })
public class MyEntity {
//...
#Column(columnDefinition = "integer unsigned", nullable = false, updatable = false, insertable = false)
private Long mySequencedValue;
//...
}
In a PostConstructComponent class:
#Component
public class PostConstructComponent {
#Autowired
private JdbcTemplate jdbcTemplate;
#PostConstruct
public void makeMyEntityMySequencedValueAutoIncremental() {
jdbcTemplate.update("alter table MyTable modify mySequencedValue int unsigned auto_increment");
}
}
I was struggling with this today, was able to solve using this
#Generated(GenerationTime.INSERT)
#Column(name = "internal_id", columnDefinition = "serial", updatable = false)
private int internalId;
#Column(name = "<column name>", columnDefinition = "serial")
Works for mySQL
I've made a separate entity table for generating id and used it in to set this non-primay key id in the service that holds that id.
Entity:
import lombok.Data;
#Entity
#Data
public class GeneralSeqGenerator {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "my_gen")
#SequenceGenerator(name = "my_gen", sequenceName= "my_seq", allocationSize = 1, initialValue = 100000)
private long seqNumber;
}
Repository:
public interface GeneralSeqGeneratorRepository extends JpaRepository<GeneralSeqGenerator, Long>{
}
Implementation of the service that holds non-primary id:
...
public void saveNewEntity(...) {
...
newEntity.setNonPrimaryId(generalSeqGeneratorRepository.save(new GeneralSeqGenerator()).getSeqNumber());
...
}
...
I've been in a situation like you (JPA/Hibernate sequence for non #Id field) and I ended up creating a trigger in my db schema that add a unique sequence number on insert. I just never got it to work with JPA/Hibernate
After spending hours, this neatly helped me to solve my problem:
For Oracle 12c:
ID NUMBER GENERATED as IDENTITY
For H2:
ID BIGINT GENERATED as auto_increment
Also make:
#Column(insertable = false)

Categories

Resources