Spring JPA insert entity with id equals null (should be auto generated) - java

Spring's JpaRepository throws an exception when I try to save entity without id:
MyConfig config = new MyConfig();
config.setValue("value");
myConfigRepository.save(config);
How to make Hibernate not include id field into insert query?
org.h2.jdbc.JdbcSQLException: NULL not allowed for column "MY_CONFIG_ID"; SQL statement:
insert into my_config (value, id) values (?, ?) [23502-190]
at org.h2.message.DbException.getJdbcSQLException(DbException.java:345)
at org.h2.message.DbException.get(DbException.java:179)
at org.h2.message.DbException.get(DbException.java:155)
at org.h2.table.Column.validateConvertUpdateSequence(Column.java:305)
at org.h2.table.Table.validateConvertUpdateSequence(Table.java:749)
at org.h2.command.dml.Insert.insertRows(Insert.java:151)
at org.h2.command.dml.Insert.update(Insert.java:114)
at org.h2.command.CommandContainer.update(CommandContainer.java:78)
at org.h2.command.Command.executeUpdate(Command.java:253)
at org.h2.server.TcpServerThread.process(TcpServerThread.java:345)
at org.h2.server.TcpServerThread.run(TcpServerThread.java:159)
at java.lang.Thread.run(Thread.java:745)
my entity:
#Entity
public class MyConfig {
#Id
#SequenceGenerator(sequenceName = "MY_CONFIG_SEQ", name = "MyConfSeq", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "MyConfSeq")
private Long id;
#Column
private String value;
//getters & setters
and repository:
public interface MyConfigRepository extends JpaRepository<MyConfig, Long>, JpaSpecificationExecutor {}

The column MY_CONFIG_ID is not part of your Hibernate mapping.
The query for insert inserts into the fields id and value. Then, there must be a third column named MY_CONFIG_ID, which is not mentioned in the entity, and thus will be inserted as null. If this column then has a not null constraint, it will fail.
If you want your id-column to be named MY_CONFIG_ID then you need to ovveride the default column name (id, same as the variable) by annotating it with #Column(name="MY_CONFIG_ID"), if you want to use the default name id you would need to remove the column (or at least the not null constraint.)

Related

Hibernate sends NULL for autogenerated field

I have entity described as
#Entity(name = "my_entity")
public class MyEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
it's table is created by Hibernate with
Hibernate: create table my_entity (id integer generated by default as identity,
i.e. it knows that field is autogenerated. Despite that having code
private MyEntity storeNewMyEntity(String fqn) {
MyEntity myEntity = new MyEntity();
myEntity.setFullyQualifiedName(fqn);
return myEntityDao.save(myEntity);
}
it translates it into
Caused by: org.h2.jdbc.JdbcSQLIntegrityConstraintViolationException: NULL not allowed for column "id"; SQL statement:
insert into my_entity (id,
Why and how to fix?
Dialect is
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
You need to update hibernate-core jar to 5.6.5.Final or any later version, current version in 5.6 branch is 5.6.7.Final and 6.0.0.Final is also already available.
Older versions don't support new versions of H2.

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.

Hibernate insert with join and foreign key

I have some problem with insert in hibernate.
Suppose I have 2 entity
#Entity
public class User{
#Id
#GeneratedValue
int user_id;
String name;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "utente_id")
list<City> cities;
}
#Entity
public class City{
int user_id
int city_id
}
if I try a create class User and add a list of city but when I try to save, receive this error:
Caused by: java.sql.SQLException: Foreign key constraint violation occurred
this because user_id in class City must be equal at user_id in User, but hibernate first inserts and then updates.
How do i revolve this?
Try first to persist the User object to DB.
Only after you persist you can get the generated value of user_id.
Then you can create the City object, set the user_id field, and persist it.

Entity Mapping - Manytomany or OneToMany

I have 2 simple tables as follows:
Property
id
value
Uid_property
id
property_id
Each row in uid_property is unique. A property can be assigned to multiple properties.
What is the best way to map this in hibernate? I am trying to decide whether this is onetomany or manytomany. Please help!
Well, you'll have to think the problem through. In order to have a ManyToMany, you need to have both tables hold references to each other. You have a property_id in Uid_property, so there can be many Uid_property records with the same property_id, but you do not have anything that allows Property to reference Uid_property.
From the Uid_property entity point of view there can be Many records for One Property. In order to reproduce the database in JPA you could simply write the following code:
#Entity
public class Property {
#Id #GeneratedValue private Long id;
}
#Entity
public class Uid_property {
#Id #GeneratedValue private Long id;
#ManyToOne
Property property;
}
This would produce the following tables:
create table Property (id bigint not null, primary key (id))
create table Uid_property (id bigint not null, property_id bigint, primary key (id))
This matches what you have said, but there is an efficiency problem. If several Uid_property records are retrieved, duplicate Property entites are created:
List<Uid_property> pl = em.createQuery("select up from Uid_property up", Uid_property.class).getResultList();
pl.forEach(uid_p->{ System.out.println(uid_p + ":" + uid_p.getProperty()); });
Gives:
model.Uid_property#5b58ed3c:model.Property#592e843a
model.Uid_property#24faea88:model.Property#592e843a
It would be more efficient if there was one Property that referenced many Uid_property entities.
#Entity
public class Property {
#Id #GeneratedValue private Long id;
#OneToMany(mappedBy="property")
private Set<Uid_property> uid_properties;
}
// This hasn't changed
#Entity
public class Uid_property {
#Id #GeneratedValue private Long id;
#ManyToOne
private Property property;
}
Now getting the entities from the database is more efficient. Properties can be joined to Uid_properties:
List<Property> pl = em.createQuery("select distinct p from Property p left join fetch p.uid_properties", Property.class).getResultList();
pl.forEach(p->{ System.out.println(p + ":" + p.getUid_properties()); });
Which gives a single Property with a Set of uid_properties for the same database schema.
model.Property#61078690:[model.Uid_property#37ebc9d8, model.Uid_property#1cb3ec38]

Invalid insert order when use #Inheritance JPA attribute

I have Eclipselink persistence provider tuned on DB2 DB. Where is 3 tables which simplified definition are listed below:
CREATE TABLE root
(
id CHAR(32) NOT NULL PRIMARY KEY,
rec_type VARCHAR(20)
);
CREATE TABLE derived
(
id CHAR(32) NOT NULL PRIMARY KEY,
...
);
ALTER TABLE derived ADD CONSTRAINT fk_derived_to_root FOREIGN KEY (id) REFERENCES root(id);
CREATE TABLE secondary
(
derived_id NOT NULL PRIMARY KEY,
...
);
ALTER TABLE secondary ADD CONSTRAINT fk_secondary_to_derived FOREIGN KEY (derived_id) REFERENCES derived(id);
Java entity classes for these entities are listed below,
RootEntity:
#javax.persistence.Table(name = "ROOT")
#Entity
#DiscriminatorColumn(name = "REC_TYPE")
#Inheritance(strategy = InheritanceType.JOINED)
public class RootEntity {
private String id;
#javax.persistence.Column(name = "ID")
#Id
#GeneratedValue(generator = "system-uuid")
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
private String principalType;
#Column(name = "PRINCIPAL_TYPE")
public String getPrincipalType() {
return principalType;
}
public void setPrincipalType(String principalType) {
this.principalType = principalType;
}
...
}
DerivedEntity:
#javax.persistence.Table(name = "DERIVED")
#Entity
#DescriminatorValue("DERIVED")
public class DerivedEntity extends RootEntity {
private SecondaryEntity secondaryEntity;
#OneToOne(mappedBy = "derived_id")
public SecondaryEntity getSecondaryEntity() {
return secondaryEntity;
}
public void setSecondaryEntity(SecondaryEntity secondaryEntity) {
this.secondaryEntity = secondaryEntity;
}
...
}
I see no derived table insertion in the test logs:
--INSERT INTO ROOT (ID, REC_TYPE) VALUES (?, ?)
bind => [241153d01c204ed79109ce658c066f4c, Derived]
--INSERT INTO SECONDARY (DERIVED_ID, ...) VALUES (?, ...)
bind => [241153d01c204ed79109ce658c066f4c, ...]
Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.3.2.v20111125-r10461): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: com.ibm.db2.jcc.am.fo: DB2 SQL Error: SQLCODE=-530, SQLSTATE=23503, SQLERRMC=SCHEM.SECONDARY.FK_SECONDARY_TO_DERIVED, DRIVER=3.57.82
So question is: why Eclipselink don't insert new record into DERIVED table prior to insertion to SECONDARY table?
P.S. Everything is working fine when no SECONDARY table (ROOT and DERIVED tables only) or no inheritance used (DERIVED tables generates id).
For inheritance JPA assumes the foreign key constraints in related table refer to the root table.
You can change your constraint to refer to the root table, or,
use a DescriptorCustomizer to set,
descriptor.setHasMultipleTableConstraintDependecy(true);
or,
customizer the OneToOneMapping to have its foreign key refer to the secondary table (JPA annotation always make it refer to the root table).
Please log a bug though, as JPA join columns should allow you to define a foreign key to the secondary table.
The reason that EclipseLink does defer the insert into the secondary table is to allow inserts to be grouped by tables to allow batch writing and avoid database deadlocks.

Categories

Resources