Table indexes/pk for Optimistic Locking in JPA - java

What is the best practice for defining table indexes for entities supporting optimistic locking?
For sure, entity id has to be part of some index in DB to enable fast lookups by id. What about version column? Does it make sense to make it part of an index?
What if we do not define DB primary key, but create an index consisting of entity id + version column? Any risk of having two rows in DB with same entity id? Say two transactions persist two entities with the same entity id in parallel?

Suppose you have an entity defined with a version column as follows:
#Entity
public class MyEntity implements Serializable {
#Id
#GeneratedValue
private Long id;
private String name;
#Version
private Long version;
//...
}
On update, the field annotated with #Version will be incremented and added to the WHERE clause, for example:
UPDATE MYENTITY SET ..., VERSION = VERSION + 1 WHERE ((ID = ?) AND (VERSION = ?))
As you can see, the version column is used in the WHERE clause, but so is the ID column (which has an index on it already as it is the primary key), so I don't think you would see much benefit in adding an index to the VERSION column.

Related

Define an inherited column to be part of composite primary key in JPA

We have an abstract base entity class that defines columns found in all of our entities (e.g. creation and modifications timestamps and related user IDs). For most of our entities, there are two related database tables: one that holds the "live" data, i.e. the current state of the entity; and one that holds the audit trail, i.e. all historical versions of the entity, as snapshots.
One of the columns shared by all entities (and defined in the base entity class) is a version number column, which is a simple running sequence that tells how many edits have been made to that particular entity. The version column is present in both the audit table and the "regular" table, but in the audit table, the version column is also part of a composite primary key, which is formed by combining the primary key of the "regular" entity and the version number. Simplified example (some entity-level annotations omitted for brevity):
#MappedSuperclass
public abstract class BaseEntity {
#Column(name = "version")
private Long version;
// Timestamps, user IDs etc.
}
#MappedSuperclass
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class PersonBase extends BaseEntity {
// All basic columns of a person defined here
}
public class Person extends PersonBase {
#Id
#Column(name = "id")
private long id;
// All references to other entities defined here
}
public class PersonAudit extends PersonBase {
#Id
#Column(name = "id")
private long id;
// FIXME: Version column should be part of composite primary key
// All foreign key columns defined here (can't have
// a direct FK relationship in an audit table, as you
// don't know which version to link to)
}
Is it possible to make the inherited version column be part of a composite primary key in the audit entities? I tried declaring the version column also in the PersonAudit table and adding the #Id annotation to it, but after that the version field in BaseEntity wasn't populated anymore. It'd be very convenient to have the field in BaseEntity as we use it in some generic Criteria Queries using the JPA static metamodel, and if we move the field declaration to the individual audit entities, we can't reference the column anymore in those queries.
P.S. We're using Hibernate, so Hibernate-specific solutions are also acceptable if JPA doesn't enable this.

Why is Hibernate using sequences

I have parent and child entities with IDs that are generated using the strategy GenerationType.TABLE because I'm working with a MySQL database.
If I create the parent without specifying an ID (i.e., create the parent for the first time), add new children to it, and save the parent, then hibernate works as expected and uses the MySQL AUTO_INCREMENT column feature.
However, If I create a parent and specify an ID (i.e., instantiating a parent that has already been persisted), add a child to it, and save the child, then hibernate issues a select sequence_next_hi_value from hibernate_sequences ... and uses that as the child's PK.
Likewise, if I instantiate a parent by getting it from the database via session.get(Parent, 1), add a new child to it, and save either the parent or the child, then hibernate uses sequences to get the child's PK.
If I create enough new parents (32767, to be exact) to run up the mysql AUTO_INCREMENT counter, there would be failures due to the primary key not being unique enough.
Here are my parent and child entities (named Location and Category, respectively):
#Entity(name="location")
public class Location {
#Id #GeneratedValue(strategy=GenerationType.TABLE) #Column(name="location_id")
private int id;
#OneToMany(mappedBy="location")
#Cascade(CascadeType.SAVE_UPDATE)
private List<Category> categories;
...
}
#Entity(name="category")
public class Category {
#ID #GeneratedValue(strategy=GenerationType.TABLE) #Column(name="category_id")
private int id;
#ManyToOne #JoinColumn(name="location_id")
private Location location;
}
Here is the hibernate code:
Hibernate: select sequence_next_hi_value from hibernate_sequences where sequence_name = 'category' for update
Hibernate: update hibernate_sequences set sequence_next_hi_value = ? where sequence_next_hi_value = ? and sequence_name = 'category'
Hibernate: insert into category (location_id, category_id) values (?, ?)
Beforehand: I have no answer why hibernate uses another strategy when persisting your child record. But I may give you some answers or hints about the other issues you described:
Regarding the strategy GenerationType.TABLE, your application relies on the database functionality of generating an ID. This may cause database round-trips for each new database record inserted. When using an alternative strategy like hi-lo, the generator will reserve a bunch of ids (for example 100 or 1000) by contacting the database only once. This may improve performance greatly. On the other side, you may loose some ID when the application is restarted. If you may life with this issue hi-lo should be considered.
Another benefit of using a strategy not relying on a database product is, that if you switch databases you may keep your mappings.

JPA 2: how to declare primary-key columns in #ElementCollection tables

in JPA2 when we are using Embed-able (Basic Type like String.. etc ) object in Entity using with #ElementCollection and #CollectionTable annotation , the new table is created , but in new table how to declare primary-key contraint in column ? following is my code
public class Employee {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private int id;
private String name;
private String salary;
#Transient
private String phnNum;
#Enumerated(EnumType.STRING)
private EmployeeType type;
#ElementCollection
#CollectionTable(name="vacations" , joinColumns=#JoinColumn(name="Emp_Id"))
private Collection<Vacation> vacationBooking;
#ElementCollection
private Set<String> nickNames;
...................
with this code the "vacation" and "employee_nickname" two tables are created in schema. but i want to declare the one primary-key column in both table . what i do for this?
It looks like a primary key per se is not supported by JPA 2.0:
From Wikibooks:
The JPA 2.0 specification does not provide a way to define the Id in the Embeddable. However, to delete or update an element of the ElementCollection mapping, some unique key is normally required. Otherwise, on every update the JPA provider would need to delete everything from the CollectionTable for the Entity, and then insert the values back. So, the JPA provider will most likely assume that the combination of all of the fields in the Embeddable are unique, in combination with the foreign key (JoinColumn(s)). This however could be inefficient, or just not feasible if the Embeddable is big, or complex.
Some JPA providers may allow the Id to be specified in the Embeddable, to resolve this issue. Note in this case the Id only needs to be unique for the collection, not the table, as the foreign key is included. Some may also allow the unique option on the CollectionTable to be used for this. Otherwise, if your Embeddable is complex, you may consider making it an Entity and use a OneToMany instead.
Do you mean that you want to assign 'id' from Employee table as foreign key to the Vacation table?
In that case, you should use #OneToMany instead of #ElementCollection

Joining a legacy table with no PK with Hibernate

I have 2 legacy tables:
CREATE TABLE A (
ID NUMBER PRIMARY KEY ,
DATA NUMBER
)
CREATE TABLE A_CONF (
A_ID NUMBER, // FK to A
INFO VARCHAR2(256)
)
Creating the JPA entity for A is straightforward. Yet, what can I do retrieve the multiple INFO fields that can be associated to an instance of A since there is no PK in A_CONF and therefore cannot create an entity for it?
Thanks for helping.
Seems like you are looking for what JPA calls an "element collection":
#Entity
public class A {
#Id
private Long id;
private Long data;
#ElementCollection
#CollectionTable(name="A_CONF", joinColumns=#JoinColumn(name="A_ID")) // A_ID would be the default join column
#Column(name="INFO")
private Set<String> infos; // using Set assuming unique values
}
You can define a primary key in your model class even if your table doesn't have one, just pick one or some columns in your model and put them as ids.

How to add a separate Primary Key to a Join Table in Hibernate

I have a question about Hibernate ManyToMany mappings. I have two classes A and B and the mapping between them is a ManyToMany mapping resolved by Hibernate:
#Entity
#Table(name="A")
public class A {
#Id
#GeneratedValue
private Long id;
#ManyToMany
#JoinTable(name="C", joinColumns=#JoinColumn(name="a_id"), inverseJoinColumns=#JoinColumn(name="b_id"))
private Set bs;
}
#Entity
#Table(name="B")
public class B {
#Id
#GeneratedValue
private Long id;
#ManyToMany(mappedBy="bs")
private Set bs;
}
As you can see, the Join Table I use is C. The foreign keys to A and B are "a_id" and "b_id". My understanding is, that Hibernate creates a composed Primary Key with a_id and b_id for table C.
I don't want to have an entity C in my model. But instead of a composed primary key on table C, I would like to have a generated ID and a unique constraint on the fields a_id and b_id.
Is it possible to tell Hibernate to use a separate primary key? Without adding an entity C?
I would appreciate any help.
Thanks a lot!
You should do iyt like this. But it can be appled only for list (not for sets)
#Entity
#TableGenerator(name="ids_generator", table="IDS")
public class Passport {
...
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name="PASSPORT_VISASTAMP")
#CollectionId(
columns = #Column(name="COLLECTION_ID"),
type=#Type(type="long"),
generator = "ids_generator"
)
private Collection<Stamp> visaStamp = new ArrayList();
...
}
I don't think it is possible. And I don't see a problem in defining a C entity.
If you have any additional information ind the join-table, it will not be accessible to you, because your Set contains the target entity - A or B.
Also, your Sets would better make use of generics - i.e. Set<A> and Set<B>.
Btw, Hibernate might not be alarmed by the fact that the table creates another entity - using your current mapping might work (disregarding completely the id column). When you said "Hibernate creates", I assumed you are generating your schema from your entity model. Now it seems it's the opposite, so give it a try.
But instead of a composed primary key on table C, I would like to have a generated ID and a unique constraint on the fields a_id and b_id.
Normally the primary key of the JoinTable is made of the combination of both foreign keys. At least, this is what JPA would generate. But if you don't use the JPA provider to generate the model and if the PK can be generated by the database (using an IDENTITY column, a trigger, etc), then you should be able to use the C table for your ManyToMany association (without having to introduce an extra entity and to transform the relation in two OneToMany). Did you actually try?

Categories

Resources