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.
Related
[INTRO]
Database : Apache Derby
JPA : EclipseLink
Hey, I was looking for answer but couldn't find any so here it goes. I'm writing simple sudoku app, and the next feature which I would like to add is saving my Sudoku boards in database, and retrieve them when it's needed. Here is UML diagram of my main two classes:
SudokuBoard.uml
The structure of my two entities are as follows :
The SudokuBoard entity:
#Entity
public class SudokuBoard implements Serializable, Cloneable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private static final long serialVersionUID = 1L;
#OneToMany(cascade=CascadeType.PERSIST)
private ArrayList<SudokuField> board;
public ArrayList<SudokuField> board() {
return board;
}
public void setBoard(ArrayList<SudokuField> board) {
this.board= board;
}
public Long etId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
The SudokuField entity:
#Entity
public class SudokuField implements Serializable, Comparable<SudokuField>,
Cloneable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Transient
private static final Logger logger =
LoggerFactory.getLogger(SudokuField.class);
#Transient
private static final long serialVersionUID = 1L;
#Basic(fetch = FetchType.EAGER)
#Column(name = "Value")
private int value;
#ManyToOne
#JoinColumn(name = "board_fk", referencedColumnName = "id")
private SudokuBoard sudokuBoard;
The execution in DAO:
#Override
public void write(SudokuBoard obj, String path) throws
FileNotFoundException, IOException {
entityManager.getTransaction().begin();
entityManager.persist(obj);
entityManager.getTransaction().commit();
}
[PROBLEM]
I wonder if there is any possiblity to not use auto generated key but instead use String as PK in SudokuBoard entity. In my application I implemented binding so I would like to save the same object of SudokuBoard which is changing over time under different names.
Hope I stated my intentions clearly. Thanks for any help and tips how it could be done.
Using String as primary key is straightforward - just declare it as such and drop the #GeneratedValue annotation.
However, changing the primary key (if that's what you mean by 'saving the same object (...) under different names') is not possible. If you try to persist/merge an existing entity under a different primary key, JPA will either raise an exception or treat it as a new entity, resulting in duplicate entries in the database. Simply put, assigning id to an entity is permanent.
All in all, I'd suggest you keep an autogenerated surrogate key and declare another unique String field for the name. Conveying business information using technical fields is rarely a good idea.
Is there any way to identify the Primary key of an entity class in my JSP page.
For Eg: If i call a function, i need to get back the #ID parameter declared in the entity class as return.
It's very obvious how to do this. Assuming that all your entities will look like about the following:
#Entity
public class MyEntity implements Serializable {
private Long id;
#Id
#GeneratedValue(...)
public Long getId() {
}
public void setId(Long id) {
return id;
}
}
you simply write myEntity.getId().
I have the following setup with Spring Data JPA and Hibernate as the persistence provider. All of my entities inherit from a base class
#MappedSuperclass
public class BaseEntity {
#Id
private id;
#Version
private String version;
//more common fields
}
For example:
#Entity
public class Foo extends BaseEntity {
}
This leads to a primary key column with name "ID" to be generated on the "FOO" table. I would like to change the naming of the primary key column. It should reflect the name of class or table. So it should be "FOO_ID" instead of just "ID".
I know that I could do this statically by using #Column(name = "FOO_ID"). But that would mean I have to do this for every Entity. Is there a more dynamic way to achieve this?
I know this is an old question, but stumbled across this looking for an answer... Eventually found this solution elsewhere:
#Entity
#AttributeOverride(name="id", column=#Column(name="FOO_ID"))
public class Foo extends BaseEntity {
}
All your subClasses will have the same ID column name because of the inheritance, you can specify a common id colum name for all subClasses in the Base entity Class
Why use inheritance then? Just do it without inheritance.
You could use getters/setters to rename your fields
Ex:
class Foo {
private Long id;
public Long getFooId() {
return this.id;
}
public void setFooId(Long fooId) {
this.id = fooId;
}
}
I have three kinds of primary keys for tables:
INT auto generated primary key which use AUTO_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.
Did not try this, but according to Hibernate's api this should not be complicated by creating custom implementation of IdentityGenerator.
It's generate method gets and object for which you are generating the value so you can check the type of the id field and return appropriate value for your primary key.
public class DynamicGenerator implements IdentityGenerator
public Serializable generate(SessionImplementor session, Object object)
throws HibernateException {
if (shouldUseAutoincrementStartegy(object)) { // basing on object detect if this should be autoincrement or not, for example inspect the type of id field by using reflection - if the type is Integer use IdentityGenerator, otherwise another generator
return new IdentityGenerator().generate(seession, object)
} else { // else if (shouldUseTextKey)
String textKey = generateKey(session, object); // generate key for your object
// you can of course connect to database here and execute statements if you need:
// Connection connection = session.connection();
// PreparedStatement ps = connection.prepareStatement("SELECT nextkey from text_keys_table");
// (...)
return textKey;
}
}
}
Having this simply use it as your generation strategy:
#MappedSuperclass
public abstract class BaseEntity<T> implements Serializable {
#Id
#GenericGenerator(name="seq_id", strategy="my.package.DynamicGenerator")
protected T id;
}
For Hibernate 4, you should implement IdentifierGenerator interface.
As above is accepted for Hibernate it should be still possible to create it in more generic way for any "jpa compliant" provider. According to JPA api in GeneratedValue annotation you can provide your custom generator. This means that you can provide the name of your custom generator and you should implement this generator for each jpa provider.
This would mean you need to annotate BaseEntity with following annotation
#MappedSuperclass
public abstract class BaseEntity<T> implements Serializable {
#Id
#GeneratedValue(generator="my-custom-generator")
protected T id;
}
Now you need to register custom generator with name "my-custom-generator" for each jpa provider you would like to use.
For Hibernate this is surly done by #GenericGenerator annotation as shown before (adding #GenericGenerator(name="my-custom-generator", strategy="my.package.DynamicGenerator" to BaseEntity class on either id field or BaseEntity class level should be sufficient).
In EclipseLink I see that you can do this via GeneratedValue annotation and registering it via SessionCustomizer:
properties.put(PersistenceUnitProperties.SESSION_CUSTOMIZER,
"my.custom.CustomIdGenerator");
public class CustomIdGenerator extends Sequence implements SessionCustomizer {
#Override
public Object getGeneratedValue(Accessor accessor,
AbstractSession writeSession, String seqName) {
return "Id"; // generate the id
}
#Override
public Vector getGeneratedVector(Accessor accessor,
AbstractSession writeSession, String seqName, int size) {
return null;
}
#Override
protected void onConnect() {
}
#Override
protected void onDisconnect() {
}
#Override
public boolean shouldAcquireValueAfterInsert() {
return false;
}
#Override
public boolean shouldOverrideExistingValue(String seqName,
Object existingValue) {
return ((String) existingValue).isEmpty();
}
#Override
public boolean shouldUseTransaction() {
return false;
}
#Override
public boolean shouldUsePreallocation() {
return false;
}
public void customize(Session session) throws Exception {
CustomIdGenerator sequence = new CustomIdGenerator ("my-custom-generator");
session.getLogin().addSequence(sequence);
}
}
Each provider must give a way to register id generator, so you would need to implement and register custom generation strategy for each of the provider if you want to support all of them.
You can "workaround" this forcing derived class to implement method which will ensure the Id is assigned and annotate this method with #PrePersist. You can provide default implementation for classes for which the Id will be auto generated.
Somethig like:
#MappedSuperclass
public abstract class BaseEntity<T> implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
protected T id;
#PrePersist
public void ensureIdAssigned() {
ensureIdAssignedInternal();
}
public abstract void ensureIdAssignedInternal();
}
#MappedSuperclass
public abstract class AutoIdMaintaintedEntity<T> extends MaintainedEntity<T> { // provide default implementation for Entities with Id generated by #GeneratedValue(strategy=GenerationType.IDENTITY) on BaseEntity superclass
public void ensureIdAssignedInternal() {
// nothing here since the Id will be automatically assigned
}
}
#Entity
public class Table1 extends AutoIdMaintaintedEntity<Long> {
#Column
private String value;
}
#Entity
public class Table2 extends BaseEntity<String> {
#Column
private String shortDescription;
#Column
private String longDescription;
public void ensureIdAssignedInternal() {
this.id = generateMyTextId();
}
private String generateMyTextId() {
return "text id";
}
}
Inheritance hierarchies fight ORM.
So keep things simple and stay a little closer to the database implementation. Don't map a hierarchy of abstract superclasses for this, but embed annotated POJO's for chunks of shared columns. They may well turn out to be handy to work with in the rest of your code as well.
Create #Embeddable classes for the shared fields, and make the class for your composite ID #Embeddable too.
#Embeddable
public class Maintained implements Serializable{
private String maintainedBy;
private String updatedBy;
// getters and setters
}
#Embeddable
public class CompositeId implements Serializable{
#Column
private int id1;
#Column
private int id2;
...
}
The simplest version of your implementation classes then look like this:
#Entity
public class Table1 {
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Id
protected Long id;
#Version
private String version;
#Embedded
private Maintained maintained;
...
public Maintained getMaintained(){
return maintained;
}
}
For the String ID, no auto generation:
#Entity
public class Table2 {
#Id
private String id;
#Column
private String shortDescription;
#Column
private String longDescription;
...
}
And the composite ID as #EmbeddedId:
#Entity
public class Table3 {
#EmbeddedId
private CompositeId id;
#Version
private String version;
#Column
private int amount;
...
}
As an extra benefit, you can mix and match adding more of these traits if you like, since you're no longer constrained by the inheritance tree.
(But you can keep the hierarchy in place containing getters and setters and default delegates if existing code relies on it and/or benefits from it.)
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).