I have a setup with Oracle XE 10g, Hibernate 3.5, JPA 2.0. There is a simple table with a primary key, which is generated by a database trigger at insertion. The trigger gets the value from a sequence. The trigger/sequence construction was made by Oracle XE.
The actual question is: How do I get the current value of the Id in my entity after a EntityManager.persist?
I tried:
@Id
private long id;
-> id is 0
;
@Id
@Generated(GenerationTime.ALWAYS)
@Column(insertable = false, updatable = false)
private long id;
-> id is 0
;
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private long id;
-> Fails because Hibernate is trying to query the a sequence directly (which does not exist).
IDs are generated in the database in the first two approaches, but I don't have the value in my object.
Simply, @Id: This annotation specifies the primary key of the entity. @GeneratedValue: This annotation is used to specify the primary key generation strategy to use. i.e Instructs database to generate a value for this field automatically. If the strategy is not specified by default AUTO will be used. GenerationType enum defines four strategies: 1.
When a new row has to be entered, a new id will be generated and used that was not originally in the table. so auto incrementing of id is it? @404 I think it depends on the database. With MySQL that's what it seems like, but it might be different with other DBs. Simply, @Id: This annotation specifies the primary key of the entity.
I know, how I can realize generation of ID column through trigger and sequence, something like this: CREATE OR REPLACE TRIGGER TR1 BEFORE INSERT ON TB1 FOR EACH ROW BEGIN SELECT SQ1.nextval INTO :new.primary_key_column FROM dual; END;
The @GeneratedValueannotation is used to specify how the primary key should be generated. In your example you are using an Identitystrategy which Indicates that the persistence provider must assign primary keys for the entity using a database identity column.
Contrary to @JB Nizet's comments, I can actually think of many reasons why we'd let a trigger assign IDs: execution of stored procs, manual execution of SQLs and running native queries in Hibernate, just to name a few.
I personally found the following solution quite satisfactory. It lets Hibernate find the max ID and have it incremented every time an insert statement is called. But when the statement hits the database, the ID is ignored and overridden by the one generated by the trigger, so there's no uniqueness in a cluster problem:
@Id
@GeneratedValue(generator="increment")
@GenericGenerator(name="increment", strategy = "increment")
private Long id;
The biggest drawback is @GenericGenerator is a Hibernate annotation, so you lose JPA's portability. It's also not clear to the programmers that this ID is actually linked to a sequence.
Another alternative is to modify the trigger to only increment sequence when ID is null. See "Hibernate issue with Oracle Trigger for generating id from a sequence". In most cases, this is the best solution because it clearly shows the ID is linked to a sequence. My only gripe is it gives user/hibernate the option to insert any ID without actually inquiring the sequence in the first place.
Unless you are bound to the trigger, that seems like a level of obfuscation over the sequence, and it seems to go outside the normal Hibernate lifecycle. Why not directly call the sequence:
@SequenceGenerator(name="alias_for_my_sequence", sequenceName="seq_name_in_oracle")
@GeneratedValue(strategy=GenerationType.SEQUENCE, generator="alias_for_my_sequence")
@Id
private Long id;
Then you will get the value back, as Hibernate is directly involved in the generation, and there isn't something happening after the fact.
You can exchange the db generated key within the Oracle session. For this, the insert trigger must call
dbms_session.set_identifier(new_id)
From your code, you can retrieve the new key value with the query
String sql = "SELECT sys_context('USERENV', 'CLIENT_IDENTIFIER') FROM dual";
Query query = em.createNativeQuery(sql);
String new_id = (String) query.getSingleResult();
It is important to use the same EntityManager instance for persisting the new entity and retrieving the generated key value.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With