JPA CrudRepository save() not populating primary key after save - spring-boot

I have very strange issue here. I am using composite primary key with #IdClass in my entities. It is working fine in every case, except save. After saving the entity, JPA is not firing SELECT query to select inserted data, and not merging the result. Though data is getting saved in database successfully. Also there are no errors. Below is some of the code which can help in debugging the issue:
AbstractEntity.java
#MappedSuperclass
#IdClass(PrimaryKey.class)
public abstract class AbstractEntity implements Serializable {
/** The Constant serialVersionUID. */
private static final long serialVersionUID = -1191422925622832672L;
/** The id. */
private String id;
...
/**
* Gets the id.
*
* #return the id
*/
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
public String getId() {
return id;
}
...
}
PrimaryKey.java
public class PrimaryKey implements Serializable {
/** The id. */
private String id;
/**
* Gets the id.
*
* #return the id
*/
#Column(name = "id")
#Convert(converter = CryptoConverter.class)
public String getId() {
return id;
}
...
}
User.java
#Entity
#Table(name = "user")
public class User extends AbstractEntity {
...
}
UserRepository.java
#Repository
public interface UserRepository extends CrudRepository<User, PrimaryKey> {
}
I have BigInt autoIncrement Id in database as primary key. But I want to expose it in encrypted form to outside world, so I have used #Converter to encrypt and decrypt it.
When I invoke userRepository.save(userEntity) from UserService, it persists the data, but does not return generated id from database.
How can I resolve this issue?
EDIT:
I have hosted demo project with this functionality here.

Since I am not seeing anywhere in your code, you need to specify Id, the strategy type and the column on the database.
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "applicant_id")

Related

Failed to convert property value of type 'java.lang.Integer' for property

I am trying to persist a class with .save method of a #Repository or #RepositoryRestResource using springboot (both case its happening the same...)
my repo:
/**
* Repository interface to do queries for UserBackoffice data.
*/
#RepositoryRestResource(path = "userBackoffice")
public interface UserBackofficeRepository
extends PagingAndSortingRepository<UserBackoffice, UserBackofficeId> {
}
my entity is this class:
/**
* DAO for UserBackoffice table in Database.
*/
#Data
#NoArgsConstructor
#AllArgsConstructor
#JsonInclude(Include.NON_NULL)
#Entity
#Table(uniqueConstraints = {#UniqueConstraint(name = "unq_user_id_constraint_01",
columnNames = {"user_id", "core_user_id", "backoffice_id"})})
#IdClass(UserBackofficeId.class)
public class UserBackoffice implements Serializable {
/**
* serial version.
*/
private static final long serialVersionUID = 1L;
/**
* user associated to the backoffice identifier.
*/
#Id
#ToString.Exclude
#EqualsAndHashCode.Exclude
#JsonBackReference(value = "user")
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "user_id")
private User user;
/**
* Backoffice Id.
*/
#Id
#ToString.Exclude
#EqualsAndHashCode.Exclude
#JsonBackReference(value = "backofficeId")
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "backoffice_id")
private Backoffice backofficeId;
/**
* Core user identifier update from Backoffice.
*/
#Column(name = "core_user_id")
private String coreUserId;
}
/**
* Artificial class to support product backoffice composite key.
*/
#Data
#NoArgsConstructor
#AllArgsConstructor
public class UserBackofficeId implements Serializable {
/**
* serial version.
*/
private static final long serialVersionUID = 1L;
/**
* user associated to the backoffice identifier.
*/
private User user;
/**
* Backoffice Id.
*/
private Backoffice backofficeId;
}
/**
* DAO for backoffice table in Database.
*/
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
#JsonInclude(Include.NON_NULL)
#Entity(name = "backoffice")
public class Backoffice implements Serializable {
/**
* serial version.
*/
private static final long serialVersionUID = 9077401445066055280L;
/**
* backoffice Id.
*/
#Id
#Column(name = "id")
private Integer id;
/**
* backoffice name.
*/
#NotEmpty
#Size(max = 100)
#Column(name = "name")
private String name;
/**
* list of user backoffice.
*/
#LazyCollection(LazyCollectionOption.TRUE)
// #JsonManagedReference
#OneToMany(cascade = CascadeType.ALL, targetEntity = UserBackoffice.class,
mappedBy = "backofficeId")
private List<UserBackoffice> userBackoffice;
}
when i try to do a simple test like this:
#Test
void saveUserBackOffice() throws StreamReadException, DatabindException, IOException {
PodamFactory factory = new PodamFactoryImpl(); //factory for dummy objects
User user = factory.manufacturePojo(User.class);//create a dummy user
Backoffice backoffice = factory.manufacturePojo(Backoffice.class); //create a dummy backoffice
UserBackoffice userbackoffice = new UserBackoffice(user, backoffice, "01");
UserBackoffice backofficeResponseAfterSave = userBackRepo.save(userbackoffice);
assertEquals(userbackoffice, backofficeResponseAfterSave);
}
i get this error message when save execute and the transaction is rollbacked:
"Failed to convert property value of type 'java.lang.Integer' to required type 'es.kukenbank.microservice.crypto.repository.entity.Backoffice' for property 'backofficeId'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.lang.Integer' to required type 'es.kukenbank.microservice.crypto.repository.entity.Backoffice' for property 'backofficeId': no matching editors or conversion strategy found"
i really dont see what is happening because user and backoffice dummy object have value according his atribute types correctly.

EntityManager persist multiple relationship

I have spring boot rest api, I have persisted 1 table successfully, but when I tried to persist object which has 2 another relations and I got error:
o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 1364, SQLState:
HY000
o.h.engine.jdbc.spi.SqlExceptionHelper : Field 'id' doesn't have a default value
here is my entity and entity manger persistance:
#Entity
#Table(name="booking")
public class Booking {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="id")
private int id;
#Column(name="description")
private String description;
#OneToMany(mappedBy="booking",cascade = CascadeType.ALL)
private List<CategoriesBooking> bookingInfos = new ArrayList<>();
#Entity
#Table(name="category_booking")
public class CategoriesBooking {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="id")
private int id;
#Column(name = "name")
private String name;
#ManyToOne(fetch=FetchType.LAZY, cascade=CascadeType.ALL)
#JoinColumn(name="booking_id")
private Booking booking;
#OneToMany(mappedBy="categoriesBooking",cascade = CascadeType.ALL)
private List<OptionsBooking> options = new ArrayList<>();
#Entity
#Table(name="options_booking")
public class OptionsBooking {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="id")
private int id;
#Column(name="name")
private String name;
#ManyToOne(fetch=FetchType.LAZY, cascade=CascadeType.ALL)
#JoinColumn(name = "catebooking_id")
private CategoriesBooking categoriesBooking;
#Transactional
#Repository
public class Services{
#PersistenceContext
protected EntityManager entityManager;
public Services() {
}
public boolean add(Booking booking){
try {
entityManager.persist(booking);
return true;
} catch (Exception e) {
entityManager.getTransaction().rollback();
}
return false;
}
}
data:
{description: 'test for persist',
bookingInfos:[{
name:'test1',
options:[{
name: 'test1-test1'
}]
}]
}
I update for use MySQL
GenerationType.AUTO chooses an ID generation strategy suitable for your database. What it actually picks depends on the database you are using. But judging from the error message it assumes the id column to be some kind of IDENTITY field which creates a unique value upon insertion.
And it seems your schema doesn't declare the id column in this way.
The obvious fix is to change that.
Sometimes changes made to the model or to the ORM may not reflect accurately on the database even after an execution of SchemaUpdate.
If the error actually seems to lack a sensible explanation, try recreating the database (or at least creating a new one) and scaffolding it with SchemaExport.

Spring JPA saving distinct entities with composite primary key not working as expected, updates same entity

I have a logic that saves some data and I use spring boot + spring data jpa.
Now, I have to save one object, and after moment, I have to save another objeect.
those of object consists of three primary key properties.
- partCode, setCode, itemCode.
let's say first object has a toString() returning below:
SetItem(partCode=10-001, setCode=04, itemCode=01-0021, qty=1.0, sortNo=2, item=null)
and the second object has a toString returning below:
SetItem(partCode=10-001, setCode=04, itemCode=01-0031, qty=1.0, sortNo=2, item=null)
there is a difference on itemCode value, and itemCode property is belonged to primary key, so the two objects are different each other.
but in my case, when I run the program, the webapp saves first object, and updates first object with second object value, not saving objects seperately.
(above image contains different values from this post question)
Here is my entity information:
/**
* The persistent class for the set_item database table.
*
*/
#Data
#DynamicInsert
#DynamicUpdate
#Entity
#ToString(includeFieldNames=true)
#Table(name="set_item")
#IdClass(SetGroupId.class)
public class SetItem extends BasicJpaModel<SetItemId> {
private static final long serialVersionUID = 1L;
#Id
#Column(name="PART_CODE")
private String partCode;
#Id
#Column(name="SET_CODE")
private String setCode;
#Id
#Column(name="ITEM_CODE")
private String itemCode;
private Double qty;
#Column(name="SORT_NO")
private int sortNo;
#Override
public SetItemId getId() {
if(BooleanUtils.ifNull(partCode, setCode, itemCode)){
return null;
}
return SetItemId.of(partCode, setCode, itemCode);
}
#ManyToMany(fetch=FetchType.LAZY)
#JoinColumns(value = {
#JoinColumn(name="PART_CODE", referencedColumnName="PART_CODE", insertable=false, updatable=false)
, #JoinColumn(name="ITEM_CODE", referencedColumnName="ITEM_CODE", insertable=false, updatable=false)
})
private List<Item> item;
}
So the question is,
how do I save objects separately which the objects' composite primary keys are partially same amongst them.
EDIT:
The entity extends below class:
#Setter
#Getter
#MappedSuperclass
#DynamicInsert
#DynamicUpdate
public abstract class BasicJpaModel<PK extends Serializable> implements Persistable<PK>, Serializable {
#Override
#JsonIgnore
public boolean isNew() {
return null == getId();
}
}
EDIT again: embeddable class.
after soneone points out embeddable class, I noticed there are only just two properties, it should be three of it. thank you.
#Data
#NoArgsConstructor
#RequiredArgsConstructor(staticName="of")
#Embeddable
public class SetGroupId implements Serializable {
//default serial version id, required for serializable classes.
private static final long serialVersionUID = 1L;
#NonNull
private String partCode;
#NonNull
private String setCode;
}
Check howto use #EmbeddedId & #Embeddable (update you might need to use AttributeOverrides in id field, not sure if Columns in #Embeddable works).
You could create class annotated #Embeddable and add all those three ID fields there.
#Embeddable
public class MyId {
private String partCode;
private String setCode;
private String itemCode;
}
Add needed getters & setters.
Then set in class SetItem this class to be the id like `#EmbeddedId´.
public class SetItem {
#EmbeddedId
#AttributeOverrides({
#AttributeOverride(name="partCode",
column=#Column(name="PART_CODE")),
#AttributeOverride(name="setCode",
column=#Column(name="SET_CODE"))
#AttributeOverride(name="itemCode",
column=#Column(name="ITEM_CODE"))
})
MyId id;
Check also Which annotation should I use: #IdClass or #EmbeddedId
Be sure to implement equals and hashCode in SetGroupId.
Can you provide that class?

Spring data jpa inheritance - table per class not working

I have an abstract entity.
#Entity
#Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
#EntityListeners(AuditingEntityListener.class)
public abstract class AbstractEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
protected long id;
#CreatedBy
protected String createdBy;
#CreatedDate
protected Date creationDate;
#LastModifiedBy
protected String modifiedBy;
#LastModifiedDate
protected Date lastModifiedDate;
}
And 2 concrete implementations of this class:
Class A:
#Entity
#Table(name = "A")
public class A extends AbstractEntity {
#Column(name = "NAME", nullable = false)
private String name;
#Column(name = "PRIORITY", nullable = false)
private int priority;
}
Class B:
#Entity
#Table(name = "B")
public class B extends AbstractEntity {
#Column(name = "PLACE", nullable = false)
private String place;
#Column(name = "DISTANCE", nullable = false)
private int distance;
}
And a common repository interface:
#NoRepositoryBean
public interface IRepository extends Repository<AbstractEntity, Long> {
/**
* Method to query by unique id/PK.
* #param id
* #return Entity with id "id"
*/
#Query("select entity from #{#entityName} as entity where entity.id = ?1")
public AbstractEntity findById(long id);
/**
* Insert method
* #param abstractEntity
* #return modified entity after insertion
*/
public AbstractEntity save(AbstractEntity abstractEntity);
/**
* select all records from the table
* #return list of all entities representing records in the table
*/
#Query("select entity from #{#entityName} as entity")
public List<AbstractEntity> findAll();
/**
* delete record by id
* #param id
*/
public void deleteById(long id);
}
And each class has it's own repository which extends the generic repository:
public interface ARepository extends IRepository {
}
public interface BRepository extends IRepository {
}
When I invoke findAll() on ARespository, I get the records in both ARepository and BRepository. Since, the inheritance type is specified as TABLE_PER_CLASS, I assumed that a findAll() would only pick records from that table. I even added a query to the findAll() method to detect entity type and pick records appropriately, but this doesn't seem to be doing anything. Is there something I'm missing here?
I'm using Hibernate as my underlying persistence framework and am working on HSQLDB.
Thanks,
Aarthi
The typing of your repositories is incorrect change it to.
#NoRepositoryBean
public interface IRepository<Entity extends AbstractEntity> extends Repository<Entity, Long> {
}
public interface ARepository extends IRepository<A> {
}
public interface BRepository extends IRepository<B> {
}

hibernate Mapping One to many relation ship between primary key and composite key

I am struggling with a hibernate mapping problem of mapping One to many relation ship between Primary key of Order Table and composite key of Product Cart with some extra columns
public class OrderDetails implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
#Id
#Column(name="ORDERID")
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer orderId;
#Column(name="ORDER_DATE")
private Date orderDate= new Date();
//other fields and getter setter
.....
.....
Product Cart table has a composite key CART ID and PRODUCT ID
#Entity
#Table(name="PRODUCT_CART")
#AssociationOverrides({
#AssociationOverride(name="pk.shopCart", joinColumns=#JoinColumn(name="CARTID")),
#AssociationOverride(name="pk.product", joinColumns=#JoinColumn(name="PRODUCTID"))
})
public class ProductCart implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
#EmbeddedId
private ProductCartId pk = new ProductCartId();
#Column(name="QUANTITY")
private Integer selectedQuantity=1;
#Column(name="TOTAL")
private double total=0.0;
//other fields and getter setter
.....
.....
I tried following but not working
#Entity
#Table(name="PRODUCTCART_ORDERDETAILS")
#AssociationOverrides({
#AssociationOverride(name="pcoPK.orderDetails",joinColumns=#JoinColumn(name="ORDERID")) ,
#AssociationOverride(name="pcoPK.pk", joinColumns=
{#JoinColumn(name="pk.shopCart",referencedColumnName="CARTID"),
#JoinColumn(name="pk.product",referencedColumnName="PRODUCTID") }) })
public class ProductCartOrder implements Serializable {
/**
*
*/
private static final long serialVersionUID = -2348674131019001487L;
#EmbeddedId
private ProductCartOrderId pcoPK = new ProductCartOrderId();
#Column(name="QUANTITY")
private Integer quantity;
#Column(name="PRICE")
private double price;
#Transient
public OrderDetails getOrderDetails(){
return getPcoPK().getOrderDetails();
}
public void setOrderDetails(OrderDetails orderDetails){
getPcoPK().setOrderDetails(orderDetails);
}
#Transient
public ProductCartId getProductCartId(){
return getPcoPK().getPk();
}
public void setProductCartId(ProductCartId pk){
getPcoPK().setPk(pk);
}
Can someone please help me to implement this? Below is the error message
Caused by: org.hibernate.AnnotationException: Illegal attempt to define a #JoinColumn with a mappedBy association: pcoPK.pk
at org.hibernate.cfg.Ejb3JoinColumn.buildJoinColumn(Ejb3JoinColumn.java:152)
at org.hibernate.cfg.Ejb3JoinColumn.buildJoinColumns(Ejb3JoinColumn.java:127)
at org.hibernate.cfg.AnnotationBinder.processElementAnnotations(AnnotationBinder.java:1212)
at org.hibernate.cfg.AnnotationBinder.fillComponent(AnnotationBinder.java:1841)
at org.hibernate.cfg.AnnotationBinder.bindId(AnnotationBinder.java:1878)
After lot of research I could not find the solution I done it in another way.
I created Many to many relationship between OrderDetails and Product with some extra columns ID, price, quantity and inserted value manually for each element in product cart thorugh a for loop.
public class Product implements Serializable {
#OneToMany(mappedBy="product")
private Set<ProductOrder> productOrder;
...//other fields and getter setter
}
public class OrderDetails implements Serializable {
#OneToMany(mappedBy="orderDetails")
private Set<ProductOrder> productOrder;
...//other fields and getter setter
}
public class ProductOrder {
#Id
#Column(name="PRODUCT_ORDER_ID")
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int prductOrderId;
#ManyToOne
private OrderDetails orderDetails;
#ManyToOne
private Product product;
...//other fields and getter setter
}
In my controller class where I wanted to save the products of ProductCart I did following
List<ProductCart> productList = new ArrayList<ProductCart>();
productList=productCartService.getCartProducts(shopCart);
ProductOrder orderedProducts = new ProductOrder();
for (ProductCart productCarts : productList) {
orderedProducts.setOrderDetails(orderDetails);
orderedProducts.setProduct(productCarts.getPk().getProduct());
orderedProducts.setPrice(productCarts.getPk().getProduct().getPrice());
orderedProducts.setQuantity(productCarts.getSelectedQuantity());
productOrderService.addOrderProducts(orderedProducts);
}

Resources