JPA Spring Boot postgresql : Insert data with the last ID - postgresql

I have a MyClass entity with an auto increment id at the time of the project deployment I have an init.sql file to initialize the MY_CLASS table by adding two lines
the problem when I use my web service REST to insert a new line in the MY_CLASS table I have an error message of duplicate key of id 1 for the first click and id 2 on the second click but after the POST goes without problems. To solve this problem I can add the following line in my init.sql file
ALTER SEQUENCE MY_CLASS_id_seq RESTART WITH 3;
My question: How can I configure my POST to persist the data with the last id because whenever I can insert data with SQL.
#Entity
#Cacheable
#Getter
#Setter
#Table(name = "MY_CLASS")
public class MyClass {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotNull
private String label;
}
#RepositoryRestResource(collectionResourceRel = "clazz", path = "clazz")
public interface MyClassRepository extends PagingAndSortingRepository<MyClass, Long> {
}
init.sql
INSERT INTO public.MY_CLASS (label) values('label_1');
INSERT INTO public.MY_CLASS (label) values('label_2');

Wouldn't you use the sequence generator, would you?
public class MyClass {
public static final String SEQUENCE_NAME = "MY_CLASS_id_seq";
#Id
#GeneratedValue(strategy = SEQUENCE, generator = SEQUENCE_NAME)
#SequenceGenerator(name = SEQUENCE_NAME, sequenceName = SEQUENCE_NAME)
private Long id;
#NotNull
private String label;
}

Related

JPA entity sequence generating

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.?

JPA repository retrieve custom primary key value after save

I have an entity class pointing to postgresql table. Below is table structure. The paymentreferencenumber is the PK which is populated by a trigger. id field is the sequence generated field. When i try to save in this table using JPARepository save method it inserts the first record. But after that it fails due to the primary key constraint. Since PK is a string type and generated using trigger I am specifying generator strategy as 'select'. Can anyone help me with this blocker and point me in the right direction. Thanks
Table structure --
custId serial not null,
paymentreferencenumber varchar(32) not null
constraint customers1_pkey
primary key,
firstname varchar(255),
lastname varchar(255)
Entity class --
#Entity
#Table(name = "customersnew")
public class Customer implements Serializable {
private static final long serialVersionUID = -1L;
#GeneratedValue(generator = "seq")
#GenericGenerator(name="seq", strategy="sequence", parameters = { #Parameter(name="key", value = "customersnew_custid_seq")})
#Column(name = "custid")
private long id;
#Id
#GeneratedValue(generator = "trigger_generated")
#GenericGenerator(name="trigger_generated", strategy="select", parameters = { #Parameter(name="key", value = "id")})
#Column(name = "paymentreferencenumber")
private String refNum;
#Column(name = "firstname")
private String firstName;
#Column(name = "lastname")
private String lastName;
}
--- Controller using JPA save
#RestController
public class CustomerController {
#Autowired
CustomerRepository repository;
EntityManagerFactory emf;
public CustomerController(CustomerRepository repository, EntityManagerFactory emf) {
this.repository = repository;
this.emf = emf;
}
#PostMapping("/create")
public String create(#RequestBody CustomerUI customer){
// save a single Customer
Customer returnObj = repository.saveAndFlush(new Customer(customer.getFirstName(), customer.getLastName()));
PersistenceUnitUtil util = emf.getPersistenceUnitUtil();
Object retObj = util.getIdentifier(returnObj);
return "Customer is created";
}
If you don't specify an id generation strategy, Hibernate will use GenerationType.AUTO. This will result in any of
AUTO - either identity column, sequence or table depending on the
underlying DB.
If you look here, you'll notice all of those generate ids of type long, short or int, not of type String.
Say you wanted a String UUID as an id, you could use
#Id
#GeneratedValue(generator = "uuid")
#GenericGenerator(name = "uuid", strategy = "uuid2")
#Column(name = "paymentreferencenumber")
private String refNum;

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.

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, Mixed surrogate key with foreign key and sequence number

I've got two tables:
DOCUMENT
--------
DOC_ID (PK)
.
.
.
SECTION
-------
DOC_ID (FK, PK)
SECTION_NUM (PK)
.
.
.
Entries in the database might look like this:
Document:
DOC_ID | . . .
--------------
1 | . . .
2 | . . .
Section:
DOC_ID | SECTION_NUM | . . .
---------------------------
1 | 1 | . . .
1 | 2 | . . .
1 | 3 | . . .
2 | 1 | . . .
Document has a generated Id on DOC_ID, while Section has a composite primary key over DOC_ID and SECTION_NUM.
SECTION_NUM is a locally(application) generated sequence number starting fresh for every document.
My entity classes look as follows:
#Entity
#Table(name = "DOCUMENT")
public class Document implements java.io.Serializable {
#Id
#Column(name = "DOC_ID", nullable = false)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "DocIdSeq")
#SequenceGenerator(name = "DocIdSeq", sequenceName = "DOC_ID_SEQ", allocationSize = 1)
private Long docId;
}
#Entity
#Table(name = "SECTION")
#IdClass(SectionId.class)
public class Section implements java.io.Serializable {
#Id
#Column(name = "DOC_ID", nullable = false)
private Long docId;
#Id
#Column(name = "SECTION_NUM", nullable = false)
private Integer sectionNum;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "DOC_ID")
private Document document;
}
public class SectionId implements java.io.Serializable {
private Long docId;
private Integer sectionNum;
}
When inserting a new Document and related Section, I do the following:
Document doc = new Document();
Section section = new Section();
section.setDocument(doc);
section.setSectionNum(1);
entityManager.persist(doc);
When persisting I get an exception stating that NULL is not allowed for column SECTION_NUM.
I'm using OpenEJB (which relies on OpenJPA behind the scenes for unit testing), and found when stepping through OpenJPA code that it successfully persists the Document object, but when it comes to the Section object it creates a new instance reflectively and sets all fields to null, so losing the sectionNum value, before linking it to the Document object persisted earlier.
Unfortunately I can't change the DB schema, as it's a legacy system.
Has anybody done something similar and got it working?
I've been meaning to update this for some time, but been too busy...
Ok, so it turns out this isn't really possible with JPA.
However, there is a workaround.
Previously I mentioned that the Document class looks like this.
#Entity
#Table(name = "DOCUMENT")
public class Document implements java.io.Serializable {
#Id
#Column(name = "DOC_ID", nullable = false)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator =
"DocIdSeq")
#SequenceGenerator(name = "DocIdSeq", sequenceName = "DOC_ID_SEQ", allocationSize = 1)
private Long docId;
}
That was just a shortened version to clarify the issue.
The real class has a collection of Sections too:
#Entity
#Table(name = "DOCUMENT")
public class Document implements java.io.Serializable {
#Id
#Column(name = "DOC_ID", nullable = false)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator =
"DocIdSeq")
#SequenceGenerator(name = "DocIdSeq", sequenceName = "DOC_ID_SEQ", allocationSize = 1)
private Long docId;
#OneToMany
private Set<Section> sections = new HashSet<Section>(0);
}
If Section had a simple primary key, JPA would easily handle the relationship, as it would accept an id from the application, or generate it from a sequence, but it won't do both with one id.
So, the solution is to manage the relationship yourself, and add a lifecycle function:
#Entity
#Table(name = "DOCUMENT")
public class Document implements java.io.Serializable {
#Id
#Column(name = "DOC_ID", nullable = false)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator =
"DocIdSeq")
#SequenceGenerator(name = "DocIdSeq", sequenceName = "DOC_ID_SEQ", allocationSize = 1)
private Long docId;
#Transient
private Set<Section> sections = new HashSet<Section>(0);
#PostPersist
public void updateChildIds() {
for (Section section : this.sections) {
section.getId().setDocId(this.docId);
}
}
}
As you can see, the Section relationship is now Transient, meaning JPA won't manage it.
After persisting a Document, the framework will call the updateChildIds function, where you manually update the Section id's with the newly persisted Document id's.
This could be demonstrated in the following facade:
#Stateless
public void DocumentFacade implements DocumentFacadeLocal {
#PersistenceContext
private EntityManager entityManager;
public void save(Document entity) throws Exception {
this.entityManager.persist(entity);
this.entityManager.flush();
this.persistTransientEntities(entity);
this.entityManager.flush();
}
private void persistTransientEntities(CaseInstructionSheet entity) {
for (Section section : entity.getSections()) {
this.entityManager.persist(section);
}
}
}
Actually, JPA is perfectly able to handle this. The annotation you are looking for is MapsId.
In your case, in your Section, on the docId you simply need to add the following:
#MapsId("docId")
The value of the MapsId annotation is the attribute name of your compound primary key (which in this case is the same)