JPA entity sequence generating - postgresql

In spring boot JPA I tried to implement sequence generator but it is not working.
the following is my entity
#Entity
#Table(name = "role_level")
public class RoleLevel implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
#Id
#SequenceGenerator(name = "role_level_sequence", sequenceName = "role_level_id_seq",allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE,generator = "role_level_sequence")
#Column(name = "id", updatable = false)
private Long id;
#Column(name = "role_level")
private String roleLevel;
#Column(name = "role_level_description")
private String roleLevelDescription;
//getters and setters
}
when I insert value in directly through the database then next sequence from the db is not getting in jpa.it shows
org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "role_level_pkey"
Detail: Key (id)=(7) already exists.
But the console shows
Hibernate: select nextval ('role_level_id_seq')
I think its not working.
Is there any solution for this.?

Related

Spring batch JdbcCursorItemReader : reading from tables having FK relation

Here's my Reader :
private static final String SELECT_ALL_BOOKS = "SELECT * FROM BOOKS WHERE COLOR = 'yellow'";
#Bean
public JdbcCursorItemReader<BookEntity> itemReader(final DataSource dataSource) {
return new JdbcCursorItemReaderBuilder<BookEntity>()
.name("book_reader")
.sql(SELECT_ALL_BOOKS)
.dataSource(dataSource)
.rowMapper(new BeanPropertyRowMapper<>(BookEntity.class))
.build();
}
And my entity :
#Entity
#Getter
#Setter
#Table(name = "book")
#AllArgsConstructor
#NoArgsConstructor
public class BookEntity implements java.io.Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id_book")
private Integer idBook;
#Column(name = "color")
private String color;
#Column(name = "page_number")
private Integer pageNumber;
#Column(name = "read")
private Boolean read;
#ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.ALL})
#JoinColumn(name = "id_author")
private Author author;
...
...
}
Problem is : with my job executing my step, I'm having the books but with an author = null. So the foreign key is not mapped correctly, all the other String/Integer/Boolean fields are retrieved correctly.
I'm new with Spring batch so maybe I'm using a wrong kind of reader.
Any one has an idea about the problem ? Thanks
Please refer this- You are using JDBC Item reader that is mapped to native columns and properties are binded by BeanPropertyRowMapper
https://docs.spring.io/spring-batch/docs/current/reference/html/readersAndWriters.html#JdbcCursorItemReader
Change the query join with AUTHOR tables as a native query or use JPA readers to support ORM
Below reference will give repository reader
https://docs.spring.io/spring-batch/docs/current/api/org/springframework/batch/item/data/RepositoryItemReader.html
Below example give some idea to implement and its SO references
https://github.com/gpassini/Spring-Batch-RepositoryItemReader-NativeQuery-Example/tree/master/src/main

spring data JPA: primary key not sequentially auto increamenting

In spring data jpa Application I created one model with #entity annotation. I am saving that model data into table. I am auto incrementing primary key. But when I am saving data into table it's not sequentially auto incrementing.
#GeneratedValue(strategy = GenerationType.AUTO)
class file
#Entity
#Table(name="exception")
public class Exception implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "exception_seq_generator")
#SequenceGenerator(name = "exception_seq_generator", sequenceName = "exception_seq")
#Column(name="exception_id")
public Integer exceptionId;
#Column(name="status_code")
public Integer statusCode;
public String message;
public String status;
public String error;
//Getter and setter
Table
can any one tell me why primary key is not auto incrementing sequentially? why it's not taking 2,3,4.....
First of all try setting the allocationSize:
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "exception_seq_generator")
#SequenceGenerator(name = "exception_seq_generator", sequenceName = "exception_seq", allocationSize=1)
#Column(name="exception_id")
public Integer exceptionId;
Also check your current Sequence in the Database, it might have a wrong value now.
Simpler aproach:
Define the primary-key column in PostgreSQL DB as SERIAL:
CREATE TABLE xy (
id SERIAL PRIMARY KEY;
);
And annotate the Column with:
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
The SERIAL type, creates an auto-increment sequence for you and you don't have that much overhead in you JPA Entities.

Exception while persisting JPA object in DB having one to many relation

hi have two tables in picture table a and table b as follows :
#Entity
#Table(name = "A")
public class A implements Serializable {
#Id
#SequenceGenerator(name = "JOURNAL_CATEGORY_ID_GENERATOR", allocationSize = 1, sequenceName = "clm_jounal_category_config_seq")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "JOURNAL_CATEGORY_ID_GENERATOR")
#Column(name = "CLAIM_ID")
private String claimId;
#Column(name = "name")
private String name;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "country")
private List<ClaimDTLS> claimDetails;
}
B Primary Key:
#Embeddable
public class BPK implements Serializable {
#Column(name = "code")
private String code;
#Column(name = "CLAIM_ID")
private String claimId;
}
B Entity:
#Entity
#Table(name = "B")
public class B implements Serializable {
#EmbeddedId
protected BPK bpk;
#Column(name = "name")
private String name;
#MapsId("country_code")
#JoinColumn(name = "claimId", referencedColumnName = "claimId", insertable = false, updatable = false)
#ManyToOne
private A a;
}
when i try to persist object of A type in Db the value of table b claim id is not set and is intialized with zero.
Also primary key of table A is generated with a oracle sequence.
any help will be welcomed.
thanks in advance
Sequence values are numbers and when JPA use them as a generator it call the setter method of the entity PK. Now, you defined your PK as a string while you use a sequence and so no matching setter can be found. Change the type of you PK to be Long and things shall work

JPA: How to fill related entity after the master is inserted

I have two tables called SL_DOCUMENT and SL_PROPOSE. The SL_DOCUMENT has its own ID (ID_DOCUMENT) and a foreign key to SL_PROPOSE (ID_PROPOSE). The SL_PROPOSE ID column is ID_PROPOSE. The particularity is that SL_PROPOSE ID value is actually the SL_DOCUMENT.ID_DOCUMENT value. i.e., after a new SL_DOCUMENT is inserted, the related SL_PROPOSE should be inserted with the SL_DOCUMENT.ID_DOCUMENT as ID and later the same value should be used in SL_DOCUMENT.ID_PROPOSE column.
I did my JPA mapping as follows:
#Entity
#Table(name = "SL_DOCUMENT")
public class DocumentORM {
#Id
#Column(name = "ID_DOCUMENT")
#SequenceGenerator(name = "SEQ_SL_DOCUMENT", sequenceName = "SEQ_SL_DOCUMENT")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQ_SL_DOCUMENT")
private Long id;
#OneToOne(mappedBy = "document", cascade = { CascadeType.PERSIST })
// #JoinColumn(name = "ID_PROPOSE", updatable = false)
private ProposeORM propose;
// ...
}
#Entity
#Table(name = "SL_PROPOSE")
public class ProposeORM {
#Id
#Column(name = "ID_PROPOSE")
private Long id;
#MapsId
#OneToOne
#JoinColumn(name="ID_PROPOSE")
private DocumentORM document;
// ...
public ProposeORM(DocumentORM document) {
super();
this.document = document;
this.document.setPropositura(this);
}
}
To create the new instances of of DocumentORM and ProposeORM:
DocumentORM document = new DocumentORM();
ProposeORM propose = new ProposeORM(document);
And finally to insert the new Document with ProposeORM:
this.documentoDAO.insert(document);
When I really insert a document, according the snippets above, I see in the console (Websphere 8.5) the INSERT commands for the SL_DOCUMENT, SL_PROPOSE running correctly. However, when I see the tables, the column SL_DOCUMENT.ID_PROPOSE is still NULL. Even If I uncomment the #JoinColumn annotation over DocumentORM.propose, the SL_DOCUMENT.ID_PROPOSE column continues to be not filled.
The ideal would be if SL_DOCUMENT had a discriminator column and ProposeORM was a DocumentORM subclass, using the JOINED InheritanceType (there are other tables with the same kind of relationship with SL_DOCUMENT). However, these are legacy tables and it is not possible to change it.
So, what is the alternative to fill SL_DOCUMENT.ID_PROPOSE? A workaround I was thinking is fill this column using a native SQL. Do you have better ideas?
Thanks,
Rafael Afonso
The solution I see is to make ProposeORM's ID not auto-generated, since you always want it to have the ID of the document it's linked to, AND still have a join column in the document table:
#Entity
#Table(name = "SL_DOCUMENT")
public class DocumentORM {
#Id
#Column(name = "ID_DOCUMENT")
#SequenceGenerator(name = "SEQ_SL_DOCUMENT", sequenceName = "SEQ_SL_DOCUMENT")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQ_SL_DOCUMENT")
private Long id;
#OneToOne
#JoinColumn(name = "ID_PROPOSE")
private ProposeORM propose;
// ...
}
#Entity
#Table(name = "SL_PROPOSE")
public class ProposeORM {
#Id
#Column(name = "ID_PROPOSE")
private Long id;
#OneToOne(mappedBy = propose)
private DocumentORM document;
// ...
public ProposeORM(DocumentORM document) {
super();
this.id = document.getId();
this.document = document;
this.document.setPropositura(this);
}
}
You'll have to persist the document first, flush the EntityManager to make sure the document has a generated ID, and then persist the propose and set it into the document.

jpa, eclips-link 2.5.1: OneToMany not working on columns not primary key

I have these two entities:
Anagrafica
#Entity
#Access(AccessType.FIELD)
#Table(name = "S_MC_CC_USER")
#SequenceGenerator(name = "SEQ_ID", sequenceName = "SEQ_ID", allocationSize = 1)
public class Anagrafica implements Serializable{
private static final long serialVersionUID = 332466838544720886L;
#EmbeddedId
private AnagraficaId anagraficaId;
#Column(name = "USER_ID")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQ_ID")
private Long userId;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "USER_ID", updatable = false, insertable = false)
private List<Mobile> mobiles;
/**
* La classe di dominio che modella la chiave primaria di un {#link Anagrafica}
*
* #author Massimo Ugues
*
*/
#Embeddable
static public class AnagraficaId implements Serializable {
private static final long serialVersionUID = -54640203292300521L;
#Column(name = "ANAG_UTENTE")
private String bt;
#Column(name = "COD_ABI")
private String abi;
public AnagraficaId() {
super();
}
Mobile
#Entity
#Table(name = "S_MOBILE")
#SequenceGenerator(name = "SEQ_MOBILE", sequenceName = "SEQ_MOBILE", allocationSize = 1)
public class Mobile implements Serializable{
private static final long serialVersionUID = 5999493664911497370L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQ_MOBILE_DEVICE_REGISTRY")
#Column(name = "ID_MOBILE")
private Long mobileId;
#Column(name = "DEVICE_TOKEN")
private String deviceToken;
#Column(name = "DATA_INSERIMENTO")
#Temporal(TemporalType.TIMESTAMP)
private Calendar dataInserimento = Calendar.getInstance();
With eclispe-link 2.1.2 all works great, but with eclispe-link 2.5.1 I got this exception:
Caused by: javax.persistence.PersistenceException: Exception [EclipseLink-28018] (Eclipse Persistence Services - 2.5.1.v20130918-f2b9fc5): org.eclipse.persistence.exceptions.EntityManagerSetupException
Exception Description: Predeployment of PersistenceUnit [persistence-unit] failed.
Internal Exception: Exception [EclipseLink-7220] (Eclipse Persistence Services - 2.5.1.v20130918-f2b9fc5): org.eclipse.persistence.exceptions.ValidationException
Exception Description: The #JoinColumns on the annotated element [field mobiles] from the entity class [class com.intesasanpaolo.domain.entities.sub.Anagrafica] is incomplete. When the source entity class uses a composite primary key, a #JoinColumn must be specified for each join column using the #JoinColumns. Both the name and the referencedColumnName elements must be specified in each such #JoinColumn.
at org.eclipse.persistence.internal.jpa.EntityManagerSetupImpl.createPredeployFailedPersistenceException(EntityManagerSetupImpl.java:1954)
at org.eclipse.persistence.internal.jpa.EntityManagerSetupImpl.predeploy(EntityManagerSetupImpl.java:1945)
at org.eclipse.persistence.jpa.PersistenceProvider.createContainerEntityManagerFactory(PersistenceProvider.java:322)
at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:288)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:310)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1571)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1509)
... 40 more
The problem is the OneToMany association based on a foreign key that is not primary key.
Since I cannot change the database model how can I make it work?
Kind regards
Massimo
The reason it worked in a prior version was that it EclipseLink doesn't look at the fields in the mapping, but with JPA adding derived Id support, EclipseLink now validates the number of foreign keys match the number of ID fields.
James' answer here
JPA #JoinColumn issues while joining on non primary key columns
explains it that you'll need to use a descriptorCustomizer to change the JPA mapping. So you would either not map the field in JPA (mark it as #Transient) and then add a mapping in the customizer, or have the JPA mapping to use all primary key fields and then change the mapping in the customizer to only use the USER_ID->USER_ID fields.
EclipseLink customizers are shown here:
http://eclipse.org/eclipselink/documentation/2.4/jpa/extensions/a_customizer.htm
Ok, this is the Customizer I created:
public void customize(ClassDescriptor descriptor) throws Exception {
// handle the oneToManyMapping to non foreign keys
ManyToManyMapping mapping = (ManyToManyMapping) descriptor.getMappingForAttributeName("mobileDevices");
ExpressionBuilder builder = new ExpressionBuilder();
mapping.setSelectionCriteria(builder.getField("USER_ID").equal(builder.getParameter("USER_ID")));
// handle the insert statement
mapping.setInsertCall(new SQLCall(""));
}
As suggested from Chris this works great with the selection.
I had to modify the Insert Call since eclipse-link tried to create and insert statement on a mapping table that I haven't.
The problem now is on the delete: when I try to delete the collection from the source association (i.e. Cliente) as described here
Cliente.ClienteId id = new Cliente.ClienteId(abi, bt);
Cliente cliente = clienteRepository.findOne(id);
cliente.setMobileDevices(null);
I need eclipse link to delete the orphan.
The dml generated is the following:
DELETE FROM S_MC_CC_CLIENTI_S_MOBILE_DEVICE_REGISTRY WHERE ((mobileDevices_ID_MOBILE_DEVICE_REGISTRY = 13) AND ((ANAG_UTENTE = '71576493') AND (COD_ABI = '01025')))
Since I haven't the mapping table I modified the customizer adding a setDeleteCall statement :
mapping.setDeleteCall(new SQLCall("DELETE FROM S_MOBILE_DEVICE_REGISTRY WHERE USER_ID = #USER_ID"));
In this way eclipse link generates 2 dml:
DELETE FROM S_MOBILE_DEVICE_REGISTRY WHERE USER_ID = NULL
DELETE FROM S_MOBILE_DEVICE_REGISTRY WHERE (ID_MOBILE_DEVICE_REGISTRY = 13)
The first is the translation of my SQLCall, but without the correct parameter: any idea how to generate only the correct delete statement?
Kind regards.
Massimo