Get generated entity Id before persisting, Hibernate, Spring data - java

Some preconditions:
I am not using Oracle DB sequence generator. Instead of it, I rely on the Hibernate sequence generator e.x.
#Entity
#Table(name = "JPA_ENTITY_A")
#GenericGenerator(name = "system-uuid", strategy = "uuid2")
public class JpaEntityA{
#Id
#Type(type = "uuid-binary")
#GeneratedValue(generator = "system-uuid")
private UUID id;
#Column(name="NAME_WITH_ID")
String nameWithGeneratedId;
}
What I want is to persist the following generated value into the column "NAME_WITH_ID": this.nameWithGeneratedId+this.id
Is it feasible to do the following:
public String getNameWithGeneratedId(){
return this.nameWithGeneratedId+this.id;//hope that the returned value will be persisted
}
Or is it possible to retrieve in advance before persisting entity to the DB generated id? If yes, then how can I accomplish it? (based on the comments below it is not possible to do it)
Thx in advance.

You can't; the act of persisting itself is what creates the ID.

In your case, which uses a UUID generator, I think you can use the Lifecycle interface and implement the onSave method returning NO_VETO.
e.g.
#Entity
public MyEntity implements Lifecycle {
#Id
#GeneratedValue
private UUID id;
...
public boolean onSave(final Session session) throws CallbackException {
// here your entity will have an ID
return Lifecycle.NO_VETO;
}
...
}
The onSave method will be called before saving the entity and after generating the ID.

Related

JPA - How to persist an entity having auto generated id and its associated entities

I have an entity class which uses auto generated id from database (PostgreSQL). It has been persisting fine without requiring me to specify an id to it. e.g.
#Entity public class MyEntity {
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
// ... other columns
}
Now I want to add a List of associated entities owned by this entity class with uni-directional association. e.g.
#Entity public class MyEntity {
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#OneToMany(cascade = CascadeType.ALL) #JoinColumn(name="pid")
private List<SubEntity> subEntities;
// ... other columns
}
#Entity public class SubEntity implements Serializable {
#Id private Integer pid; // refer to id of MyEntity
#Id private String name; // pid, name forms a composite key for SubEntity
// ... other columns
}
Then I bumped into an issue that JPA (Hibernate in this case) was generating SQLs like:
INSERT INTO MYENTITY (...) VALUES (...)
INSERT INTO SUBENTITY (pid, ...) VALUES (null, ...)
It failed when trying to insert a null value to pid as it has not null constraint in the database schema. If I bypass this, Hibernate then generates an update statement to update the null value with the generated id from MyEntity:
UPDATE SUBENTITY SET pid = ? WHERE pid = null AND name = ?
I get that the auto generated id is not known until after the insert to MyEntity, so it updates afterward. But I wonder if there is a solution so that Hibernate does the insert to MyEntity ONLY first, get the generated id THEN does the inserts to SubEntity with the correct pid and no update afterward?
This should be possible. Please create an issue in the Hibernate issue tracker with a test case that reproduces this issue. Apart from that, I would suggest you try using a sequence generator as that is more scalable anyway.

Spring data JPA generate id on database side only before create

In my application I'm using String data JPA with PostgreSQL. And for all entities in my application I'm looking for the way, to generate ids purely on the database side.
So, for example, here my currently working version:
#Entity
#Table(name = "permissions")
public class PermissionEntity {
#Id
#SequenceGenerator(name="permSeq", sequenceName="perm_id_seq", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator="permSeq")
private Short id;
...........
}
I'm using allocationSize = 1 to avoid conflicts, because I'll have multiple instances of the application, that will work with the same database.
But in this case each time before create permission, application makes request to the database perm_id_seq sequence to receive the next value. I wanna create id purely on database side, like in case with jdbc template. To do that I set default value for the id = nextval('perm_id_seq'::regclass) and modified my code:
#Entity
#Table(name = "permissions")
public class PermissionEntity {
#Id
private Short id;
...........
}
Now it throws an exception when I'm trying to save entity:
org.hibernate.id.IdentifierGenerationException: ids for this class must be manually assigned before calling save()
The reason why is clear for me. But, can somebody tell me, is it possible to generate ids on database side as I wish at all?
Use GenerationType.IDENTITY for your id, this will tell hibernate that the id of the identity column will be generated on the DB side :
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Short id;

#GeneratedValue in Oracle with no Sequence

I have created a trigger so that my entities ids are autogenerated with a sequence each time they're inserted into my Oracle database.
The problem comes with annotating these entities for Hibernate/JPA: I need to define a #GeneratedValue annotation but I don't want to specify the sequence name -- doing that will make Hibernate query the sequence first, then insert, which is a work that is already done by the trigger.
Is there any way to skip this sequence in the #GeneratedValue with the scenario I've proposed?
Exception I get if id is not provided:
org.hibernate.id.IdentifierGenerationException: ids for this class must be manually assigned before calling save(): Pattern
Pattern class:
#Entity
#Table(name = "PATTERN")
public class Patron extends HistoricoAbstractEntity {
/**
*
*/
private static final long serialVersionUID = 1L;
#Id
#Column(name = "ID_PATTERN")
private Integer idPattern;
#Column
private String description;
#Column(name = "NEGATIVE")
private Boolean isNegative;
#Column(name = "type")
private Integer type;
#Column(name = "N_DAYS")
private Integer numDays;
... (getters & setters)
}
From what your code,
What I can tell you is that its not related to #GeneratedValue, it specifies that the hibernate takes responsibility to generate and idetifier for your entity. In your case your are generating id your self, so you have to manually set the id for that particular entity. Then you won't get this error any more, the other thing that you can try is use of #PrePersist annotate a method with this and try assigning a value to id in it. I haven't tried this but this should work according to this answer on SO.
Assign Custom Identifier
If your id is being generated by the database then you should use #GeneratedValue(strategy=GenerationType.AUTO) on your id field along with your #Id annotation.

JPA/Hibernate - shared primary key

I wanted to create bidirectional one to one relationship with shared primary key.
As it is stated here JPA Hibernate One-to-One relationship I have:
#Entity
public class UserProfileInformation {
#Id
#GeneratedValue(generator = "customForeignGenerator")
#org.hibernate.annotations.GenericGenerator(
name = "customForeignGenerator",
strategy = "foreign",
parameters = #Parameter(name = "property", value = "userEntity")
)
long id;
private long itemsPerPage;
#OneToOne(mappedBy="userProfileInformation")
private UserEntity userEntity;
...}
#Entity
#Table(name = "UserTable")
public class UserEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String publicName;
private String password;
private String emailAddress;
private String name;
private boolean active;
#OneToOne(cascade=CascadeType.ALL)
#PrimaryKeyJoinColumn
private UserProfileInformation userProfileInformation;
...}
now, when I try to persist my user in the database, I am getting org.hibernate.id.IdentifierGenerationException: null id generated for:class pl.meble.taboret.model.UserProfileInformation. Is it because, when userProfileInformation is persisted to the database userEntity doesn't have id generated at that point?
Also, what can I do to create bidirectional relationship with shared primary key in my example?
EDIT:
Requested code, this is simple controller to test the operation of persisting UserEntity.
#Controller
#RequestMapping("/test")
public class TestController {
#Autowired
UserDao userDao;
#RequestMapping(method= RequestMethod.GET)
public String t(Model model){
UserEntity entity=new UserEntity();
entity.setActive(false);
entity.setEmailAddress("a");
entity.setName("name");
entity.setPassword("qqq");
entity.setPublicName("p");
UserProfileInformation p = new UserProfileInformation(entity);
entity.setUserProfileInformation(p);
userDao.addUser(entity);
return "login";
}
}
I think the problem is with the id generation strategy. For hibernate #GeneratedValue(strategy = GenerationType.AUTO) translates into a native identifier generation. This means that hibernate expects an identity id field for the UserTable.
I don't know exactly how SQLite works in terms of identity columns, but it seems from this SO question is a little different (see the second answer).
Anyway if you plan to run your application on multiple databases is better for portability to change the id generation strategy from GenerationType.AUTO and use hibernate enhanced generators: SequenceStyleGenerator or TableGenerator. See this link in the hibernate documentation.
EDIT:
I tried to reproduce your problem, and it seems that SQLite dialect is not among the officially supported hibernate dialects. Meanwhile I tested your case with the H2 embeded database and it works as expected: your mappings are correct.
If you are using an unofficial SQLite dialect it might be a bug with this dialect.

JPA: Override Auto generated ID [duplicate]

This question already has answers here:
Bypass GeneratedValue in Hibernate (merge data not in db?)
(8 answers)
Closed 5 years ago.
I have following definition in Employee class
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "employee_id")
private Integer employeeId;
Now I want to import existing employees with existing employee IDs. Even if I set the employee ID before saving, the assigned ID is ignored and auto incremented ID is stored. How do we override this?
I had similar problem for composite key which has been explained here
I wrote my own generator to solve the issue.
public class UseExistingIdOtherwiseGenerateUsingIdentity extends IdentityGenerator {
#Override
public Serializable generate(SharedSessionContractImplementor session, Object object) throws HibernateException {
Serializable id = session.getEntityPersister(null, object).getClassMetadata().getIdentifier(object, session);
return id != null ? id : super.generate(session, object);
}
}
and use it like this: (replace the package name)
#Id
#GenericGenerator(name = "UseExistingIdOtherwiseGenerateUsingIdentity", strategy = "{package}.UseExistingIdOtherwiseGenerateUsingIdentity")
#GeneratedValue(generator = "UseExistingIdOtherwiseGenerateUsingIdentity")
#Column(unique = true, nullable = false)
protected Integer id;
You cannot override it. First of all JPA do not provide way to do it. Second problem is that GenerationType.AUTO can produce such a column type that will not accept user assigned value via SQL statement.
Perform import of data manually and do not try to use application itself as an import tool.
you must override getEmployeeId() method and use Annotation on this method
in this case JPA uses the getter methods for all fields in class and you must move other Annotaions on getter methods
In this case, it might be best to have a #MappedSuperclass
#MappedSuperclass
public AbstractEmployee {
// declare all properties, getters, setters except for the id
}
#Entity
#Table(name="EMPLOYEE")
public class Employee extends AbstractEmployee {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "employee_id")
private Integer employeeId;
...
}
#Entity
#Table(name="EMPLOYEE")
public class EmployeeWithAssignedId extends AbstractEmployee {
#Id
#Column(name = "employee_id")
private Integer employeeId;
...
}
So when you want to assign the ID manually, use the second class. I have not tested this, but I believe it should work for your situation. I don't believe there's any rule in JPA that says two classes can't have the same table name :)

Categories

Resources