Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I annotate Id column if value is generated by a trigger?

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.

like image 305
Zeemee Avatar asked Aug 18 '11 13:08

Zeemee


People also ask

What is the difference between @id and @generatedvalue annotation?

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.

What does the @ID annotation mean?

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.

How to realize generation of ID column through trigger and sequence?

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;

What is the @generatedvalueannotation used for?

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.


3 Answers

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.

like image 75
Christopher Yang Avatar answered Sep 20 '22 23:09

Christopher Yang


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.

like image 35
atrain Avatar answered Sep 20 '22 23:09

atrain


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.

like image 36
Frank Avatar answered Sep 20 '22 23:09

Frank