spring-data-jdbc update with repository save methods updates ID - java

Calling CrudRepository save() method for an entity that is NOT new creates following sql: UPDATE card SET id = ?, customer_id = ? ... WHERE id = ?
This raises exception Cannot update identity column 'id'
ID is generated by the database
used version: 1.0.6.RELEASE & 1.0.9.RELEASE
DB: mssql
Why is update statement trying to update the ID column as it is the primary key?
Entity:
import org.springframework.data.annotation.Id;
import org.springframework.data.relational.core.mapping.Column;
import org.springframework.data.relational.core.mapping.Table;
#Table("card")
public class Card {
#Id
private Long id;
#Column("customer_id")
private String customerId;
...
Repository:
public interface CardRepository extends CrudRepository<Card, String> {
}

This sounds like https://jira.spring.io/browse/DATAJDBC-262 which was fixed and released in version 1.1.M1 the current version of that development branch is 1.1.RC1.
Switching to that version should solve the problem.
Note: I've seen the exception you mention only with MS-SqlServer which isn't yet fully supported yet.

It seems that you haven't set id generation Strategy for the ID.
Add this and try if it is working.
#GeneratedValue(strategy = GenerationType.AUTO)

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.

spring-data-jdbc readOnly

I'm trying to make some field readOnly -> insert and update aka save() should not send that field to DB but the field should be populated with select.
#ReadOnlyProperty from org.springframework.data.annotation.ReadOnlyProperty does not do the trick.
versions: spring-boot: 2.2.0.RC1, spring-data-jdbc: 1.1.0.RELEASE, spring-data-commons: 2.2.0.RELEASE
db: MSSQL
spring-data-jdbc readOnly
Should it work and is there any other way to do it?
NOTE: please don't mix spring-data-jdbc with spring-data-jpa
import java.util.Set;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.ReadOnlyProperty;
import org.springframework.data.relational.core.mapping.Column;
import org.springframework.data.relational.core.mapping.MappedCollection;
public class Organization {
#Id
private Long id;
private String name;
#Column("readOnlyProperty")
#ReadOnlyProperty
private String readOnlyProperty;
#ReadOnlyProperty
#MappedCollection
private Set<Employee> employees;
}
import org.springframework.data.annotation.Id;
public class Employee {
#Id
private Long id;
private String name;
}
#Test
public void insert() {
// insert should not set readOnlyProperty
Organization organization = new Organization("org1", "readOnly");
Employee employee = new Employee("emp1");
Set<Employee> employess = new HashSet<>();
employess.add(employee);
organization.setEmployees(employess);
organizationRepository.save(organization);
}
LOG:
Executing prepared SQL statement [INSERT INTO organization (name, readOnlyProperty) VALUES (?, ?)]
Executing prepared SQL statement [INSERT INTO employee (name, organization) VALUES (?, ?)]
This is a bug.
I created DATAJDBC-431 for it and it will probably fixed in the next service release.
I didn't test, but according to this
The Column annotation and XML element defines insertable and updatable options. These allow for this column, or foreign key field to be omitted from the SQL INSERT or UPDATE statement. These can be used if constraints on the table prevent insert or update operations. They can also be used if multiple attributes map to the same database column, such as with a foreign key field through a ManyToOne and Id or Basic mapping. Setting both insertable and updatable to false, effectively mark the attribute as read-only.
#Column(name="COLUMN_NAME",updatable=false, insertable=false)
private String fieldName;
should make the field read-only.

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;

Error on compiling query: The abstract schema type 'entity' is unknown

I'm developing a game with a database connection, and I use JPA to persist my data. Here is my Game entity :
#Entity
#Table(name = "game")
public class Game implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "game_id")
private int id;
#Column(name = "name")
private String name;
#Column(name = "nbTurns")
private int nbTurns;
#Column(name = "playedOn")
#Temporal(TemporalType.TIMESTAMP)
private Date playedOn;
#ElementCollection(fetch = FetchType.EAGER)
#CollectionTable(name = "game_humans", joinColumns = #JoinColumn(name = "game_id"))
#MapKeyColumn(name = "human_id")
#Column(name = "isDead")
private Map<Human, Boolean> humans;
And here is my Human entity :
#Entity
#Table(name = "human")
public class Human implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#Column(name = "name")
private String name;
#OneToOne
private Building building;
To get the list of all the humans stored in the DB, I use this DAO, which is working very well and gets also the Building entity :
public class HumanDAO implements DAO<Human> {
// ...
public List<Human> getAllHumans() {
TypedQuery<Human> query = em.createQuery("SELECT h FROM human h ORDER BY h.name", Human.class);
return query.getResultList();
}
The problem is when I try to do the same to get the list of all the games with the JPQL query SELECT g FROM game g, I get this error :
[EL Info]: 2013-11-25 13:40:27.761--ServerSession(1943119327)--EclipseLink, version: Eclipse Persistence Services - 2.5.0.v20130507-3faac2b
[EL Info]: connection: 2013-11-25 13:40:28.151--ServerSession(1943119327)--file:/Users/amine/Documents/workspace/ZombiesServer/target/classes/_ZombiesServer login successful
[WARNING]
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.codehaus.mojo.exec.ExecJavaMojo$1.run(ExecJavaMojo.java:297)
at java.lang.Thread.run(Thread.java:724)
Caused by: java.lang.IllegalArgumentException: An exception occurred while creating a query in EntityManager:
Exception Description: Problem compiling [SELECT g FROM game g].
[14, 18] The abstract schema type 'game' is unknown.
at org.eclipse.persistence.internal.jpa.EntityManagerImpl.createQuery(EntityManagerImpl.java:1585)
at org.eclipse.persistence.internal.jpa.EntityManagerImpl.createQuery(EntityManagerImpl.java:1605)
at com.amine.zombies.DAO.GameDAO.getAllGames(GameDAO.java:80)
at com.amine.zombies.application.Application.main(Application.java:21)
... 6 more
You should have
SELECT g FROM Game g//you have game
but you have game instead of Game.
The #Table annotation is used for DB.
If you need to change the name in your JPQL, use the #Entity annotation: #Entity(name="nameUsedInJPQL") => nameUsedInJPQL is used in your JPQL.
If you do not specify anything in your #Entity, that the case-sensitive Entity class name is used.
In my case I forgot to register it in persistence.xml.
I just had the very same situation but my JPQL query was correct ! It occured in Glassfish 4.1 (build 13) (with EclipseLink).
After a few googling and some code commenting, I found out that the root cause of "The abstract schema type 'MyEntity' is unknown" was some use of Java 8 lambda code inside the entity class.
It seems that any feature of Java 8 is not (yet) supported in the version of EclipseLink that comes with GF. More info, see the bug report on that.
Hope this helps.
class name should be there not the table name in your query
SELECT g FROM Game g
The name to be used for JPQL queries is defined as the simple name of the entity class - Game or Human in your case. It can be overridden by the name attribute of the #Entity annotation. #Table is a physical mapping annotation and does not influence the entity name in the query.
It does work with human because the query string is not case-sensitive.
We got the problem due to an update of org.eclipse.persistence.eclipselink library from 2.4.0 to 2.5.1. After updating to 2.6.2 it works again.
If you are calling the persistence.xml in a bundle, then org.eclipse.persistence.jpa must be started before your bundle (or bundles). If not, you will get the same exception. You will also see that the tables have not been made in your database. It is therefore a good idea to always add org.eclipse.persistence as an imported package in your manifest.
In my case no answer help me. My JPA project was using Java 14 with EclipseLink 3.0.0 as a provider and seems as EclipseLink doesn´t support new Java versions. Switching back to Java 1.7 did the trick.

Categories

Resources