I have three kinds of primary keys for tables:
INT
auto generated primary key which useAUTO_INCREMENT
capacity from database vendor (MySQL)CHAR(X)
primary key to store a user readable value as key (where X is a number and 50 <= X <= 60)- Complex primary keys, composed by 2 or 3 fields of the table.
Also, there are some group of fields that may be present (or not):
- version,
INT
field. - createdBy,
VARCHAR(60)
field, and lastUpdatedBy,VARCHAR(60)
field (there are more fields but these covers a basic example).
Some examples of above:
- Table1
- id int primary key auto_increment
- version int
- value char(10)
- createdBy varchar(60)
- lastUpdatedBy varchar(60)
- Table2
- id char(60) primary key
- shortDescription varchar(20)
- longDescription varchar(100)
- Table3
- field1 int primary key
- field2 int primary key
- amount decimal(10, 5)
- version int
With all this in mind, I need to create a generic set of classes that supports these requirements and allows CRUD operations using Hibernate 4.3 and JPA 2.1.
Here's my current model (getters/setters avoided to shorten the code sample):
@MappedSuperclass
public abstract class BaseEntity<T> implements Serializable {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
protected T id;
}
@MappedSuperclass
public abstract class VersionedEntity<T> extends BaseEntity<T> {
@Version
protected int version;
}
@MappedSuperclass
public abstract class MaintainedEntity<T> extends VersionedEntity<T> {
@Column
protected String createdBy;
@Column
protected String lastUpdatedBy;
}
@Entity
public class Table1 extends MaintainedEntity<Long> {
@Column
private String value;
}
@Entity
public class Table2 extends BaseEntity<String> {
@Column
private String shortDescription;
@Column
private String longDescription;
}
I'm currently testing save instances of Table1
and Table2
. I have the following code:
SessionFactory sf = HibernateUtils.getSessionFactory();
Session session = sf.getCurrentSession();
session.beginTransaction();
Table1 newTable1 = new Table1();
newTable1.setValue("foo");
session.save(newTable1); //works
Table2 newTable2 = new Table2();
//here I want to set the ID manually
newTable2.setId("foo_id");
newTable2.setShortDescription("short desc");
newTable2.setLongDescription("long description");
session.save(newTable2); //fails
session.getTransaction().commit();
sf.close();
It fails when trying to save Table2
and I get the following error:
Caused by: java.sql.SQLException: Field 'id' doesn't have a default value
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:996)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3887)
The error message is obvious because a CHAR(X)
field doesn't have a default value and won't have it (AFAIK). I tried changing the generation strategy to GenerationType.AUTO
and got the same error message.
How can I remodel these classes in order to support these requirements? Or even better, how could I provide a generation strategy that depends on the key of the entity I'm saving, which could be auto generated or provided by me?
Involved technologies:
- Java SDK 8
- Hibernate 4.3.6
- JPA 2.1
- MySQL and Postgres databases
- OS: Windows 7 Professional
Note: the above may (and probably will) change in order to be supported for other implementations of JPA 2.1 like EclipseLink.