Spring data JPA generate id on database side only before create - java

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;

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.

Get generated entity Id before persisting, Hibernate, Spring data

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.

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.

Persist using stored procedure with return id

PostgreSQL 9.1, Hibernate 4.1.
How can I persist PropertyTypeEntity using stored procedure (problem in return id. GenerationType.IDENTITY)
It impossible?
#Table(name = "property_types", schema = "public", catalog = "")
#Entity
public class PropertyTypeEntity implements IEntity<Long> {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
...
}
There is at least following two ways:
Use SQLInsert, though I have no idea is there interoperability issues with JPA annotations
Create custom generator, some example can be found from this blog post.

Seam/Hibernate/JPA -- Duplicate primary key exception?

I've got an object model that is persisted using Seam and JPA (Hibernate). It looks something like this:
#Entity(name = "MyObject")
public class MyObject {
...
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq_myobj")
#SequenceGenerator(name = "seq_myobj", sequenceName = "seq_myobj")
private Long id = null;
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER, optional = false)
#NotNull
private MySubObject subObjA=null;
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER, optional = false)
#NotNull
private MySubObject subObjB=null;
...
}
#Entity(name = "MySubObject")
public class MySubObject {
...
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq_mysubobj")
#SequenceGenerator(name = "seq_mysubobj", sequenceName = "seq_mysubobj")
private Long id = null;
}
I've defined my #ManyToOne annotations correctly and everything. However, if I try and persist an instance of MyObject where both subObjA and subObjB are set, I get an exception saying I've got a duplicate primary key one of the sub obj's. What would cause this behavior? Both objects have their identifier types set to SEQUENCE, and I have no problem if I set one or the other. It's only when I set both that I get the exception.
I'm running Seam 2.2 and my backend database is PostgreSQL. Any thoughts on what could be causing this strange behavior? I thought both objects would be persisted as part of the same transaction and that the correct primary keys would be assigned automatically. Like I said, if I only set one of the objects there is no issue. It only happens when I set them both. Any help you can give would be GREATLY appreciated.
EDIT I've noticed some strange behavior in testing out various things, however. If I create MyObject programmatically and set all of its properties (including subObj) it persists with no problem. However, if I enter the properties using a form, I get the error. Could it have something to do with transactions?
If you override equals/hashCode in MySubObject class be sure that these methods only check the surrogate key id (in such a case you should avoid them completely).
If equals/hashCode methods work with some business key properties, make sure that these keys are unique before persisting.
Ran through a battery of tests and different scenarios, and have found occasions when it works. So, it looks like there is a bug in my action class when I got to submit and persist to the database.

Categories

Resources