How to persist object in db with existing primary key in Spring boot JPA? - spring

Scenario:
I do have JSON object as Menus.
//suppose this as
Menus menus = new Menus();
menus.setId(61);
menus.setUrl("test_url");
menus.setName("menu name");
repository.save(menus);
Current case:
It is working fine if db has menu row with id 61 as a result object gets updated.
While row with id=61 does not exists in db then this menu object gets persisted but with new id. ie. a new row is created with new auto generated ID.
Expected:
If menu where id = 61 does not exists in db then menus should be inserted in db with id=61
package com.rasello.auth.entity;
import lombok.*;
import org.hibernate.annotations.Type;
import javax.persistence.*;
#Entity
#Data
#NoArgsConstructor
#Table(name = "menus")
#Getter
#Setter
public class Menus {
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
private Long id;
private String name;
private String url;
}

Remove this line #GeneratedValue(strategy = GenerationType.TABLE) and try again. The #GeneratedValue does what the words say. Generates value for the ID and in case you do not need that then you should not use it.
You can read more about how to set up your Ids in this article https://www.baeldung.com/hibernate-identifiers

Related

When does an autogenerated UUID Id get generated with spring data JPA and hibernate?

Suppose you have a class with the following UUID primary key:
import javax.persistence.*;
import org.hibernate.annotations.GenericGenerator;
#Entity
public class MyEntity {
#Id
#GeneratedValue(generator = "UUID")
#GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator")
private UUID id;
// other fields and constructor omitted for brevity
}
When you create a new MyEntity(), the id property is null. That's something I'm used to when the database is responsible for generating the id. In those cases, you must save the entity first before the id is populated. In this case the underlying database is a postgres database with id column type UUID, but the application should be able to generate the Id.
Is there a reason it wouldn't generate automatically and immediately?
Reference: https://thorben-janssen.com/generate-uuids-primary-keys-hibernate/

Spring Data + View with Union return duplicate rows

i'm using Spring Boot 2.4.2 and Data module for JPA implementation.
Now, i'm using an Oracle View, mapped by this JPA Entity:
#Entity
#Immutable
#Table(name = "ORDER_EXPORT_V")
#ToString
#Data
#NoArgsConstructor
#EqualsAndHashCode(onlyExplicitlyIncluded = true)
public class OrderExportView implements Serializable {
private static final long serialVersionUID = -4417678438840201704L;
#Id
#Column(name = "ID", nullable = false)
#EqualsAndHashCode.Include
private Long id;
....
The view uses an UNION which allows me to obtain two different attributes of the same parent entity, so for one same parent entity (A) with this UNION I get the attribute B in row 1 and attribute C in row 2: this means that the rows will be different from each other.
If I run the query with an Oracle client, I get the result set I expect: same parent entity with 2 different rows containing the different attributes.
Now the issue: when I run the query with Spring Data (JPA), I get the wrong result set: two lines but duplicate.
In debug, I check the query that perform Spring Data and it's correct; if I run the same query, the result set is correct, but from Java/Spring Data not. Why??
Thanks for your support!
I got it! I was wrong in the ID field.
The two rows have the same parent id, which is not good for JPA, which instead expects a unique value for each line.
So, now I introduced a UUID field into the view:
sys_guid() AS uuid
and in JPA Entity:
#Id
#Column(name = "UUID", nullable = false)
#EqualsAndHashCode.Include
private UUID uuid;
#Column(name = "ID")
private Long id;
and now everything works fine, as the new field has a unique value for each row.

Reading #Transient fields from database

I have a spring boot application with database and entity with #Transcient field... here you have a sample code:
#Entity
#Table(name = "dogs")
#JsonInclude(Include.NON_NULL)
#ApiModel
public class Dog {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#Transient
public Boolean happy;
}
Dog dog1 = dogsRepository.findById(1);
dog1.setHappy(true);
Dog dog2 = dogsRepository.findById(1);
System.out.println("dog2 is happy = " + dog2.isHappy());
and the last line prints dog2 is happy = true on the screen. How it is possible? #Transient fields are not persisting in a database.
The method dogsRepository.findById checks the 1st level cache. If it cannot be found there it will be fetched from the database and stored in the 1st level cache. That is why the 2nd call to findById will not retrieve it from the database but from the cache instead. That is why dog1 and dog2 are the same object in your case.
That will not happen for example if you clear the cache between the findById calls or execute the calls in different transactions.

Spring Boot repository save does not work (only shows a select)

I'm facing for hours with a strange proceeding in Spring Boot when try to save a mapped entity.
The entity class with a composite key that must all be set by the user is as follows:
package model
import javax.persistence.*
#Entity
#Table(name = 'MY_TABLE')
#IdClass(MyIdClass.class)
class MyClass implements Serializable{
#Id
#Column(name = "MY_COLUMN_1")
Long column1
#Id
#Column(name = "MY_COLUMN_2")
Long column2
#Id
#Column(name = "MY_COLUMN_3")
String column3
#Id
#Column(name = "MY_COLUMN_4")
Date date1
#Column(name = "MY_COLUMN_5")
Date date2
#Column(name = "MY_COLUMN_6")
BigDecimal column6
}
#Embeddable
class MyIdClass implements Serializable{
Long column1
Long column2
String column3
Date date1;
}
The corresponding repository is:
package repository
import org.springframework.data.repository.CrudRepository
interface MyRepository extends CrudRepository<MyClass, Long>{
}
My service is:
package service
import model.MyClass
import repository.MyRepository
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service
#Service
class MyService {
#Autowired
MyRepository repository
void save(MyClass myClass) {
repository.save(myClass)
}
}
My controller mounts a MyClass object with all data set, including the composite key. When it calls the service save method the object is not inserted in the database. I saw the logs and checked that there is a SELECT in MY_TABLE instead of INSERT. I tried not to inform the composite key in the object and then the save method did an INSERT with error due to null values in the primary key.
I really don't understand why the insertion is not done when the composite key has values. How can I solve it?
I've already tried with #Transactional in service class and didn't work. I didn't do any Transaction configuration in the project since Spring Boot delivers it as default.
Thanks.
It seems you are using MyIdClass as the Id for MyClass. So, the Repository should be:
interface MyRepository extends CrudRepository<MyClass, MyIdClass>{
}
Hope this help.
I take your code sample and tried it on a sample Spring Boot project, where I was able to save to H2 DB (In memory) with #Embeddable & #EmbeddedId annotations. If you would like to verify, you can clone the GitHub repo and run the BootJpaApplication.java as a Java Application.
After execution access the H2 console with the below link from local where table details can be verified.
http://localhost:8080/h2-console
https://github.com/sujittripathy/springboot-sample.git
Hope the detail helps:)

Retrieve entity auto generated Id

I am trying to find a way to retrieve the auto generated Id of an entity that is persisted in the database via cascade. I am using Hibernate 4.1.9, Spring data 1.2 and Spring framework 3.2.1. Here are the entities in question : Location, Home, Room.
Location parent class
#Entity
#Table(name = "location")
#Inheritance(strategy = InheritanceType.JOINED)
public class Location implements Serializable
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "location_id", unique = true)
private long uuid;
// other attributes and methods not relevant
}
Home class extending a Location, referencing a set of Rooms
#Entity
#Table(name = "home")
#Inheritance(strategy = InheritanceType.JOINED)
#PrimaryKeyJoinColumn(name = "home_id")
public class Home extends Location implements Serializable
{
#OneToMany(mappedBy = "containingHome", cascade = {CascadeType.ALL}, orphanRemoval = true)
private Set<Room> rooms;
// other attributes and methods not relevant
}
and finally the Room class referencing a Home object
#Entity
#Table(name = "room")
#PrimaryKeyJoinColumn(name = "room_id")
public class Room extends Location implements Serializable
{
#ManyToOne()
#JoinColumn(name = "home_id")
protected Home containingHome;
// other attributes and methods not relevant
}
I am using Spring data to create Repositories for the entities.
LocationRepository
public interface LocationRepository extends JpaRepository<Location, Long>
{ }
The problem I am having is that I need the id in order to be able to retrieve the different objects from the database and that is generated automatically. The only way I can access the id through the element is if I get the managed object when I save it to the database. But if I try to save each location in turn like so:
Home home = new Home();
home = locationService.save(home) // service that just calls locationRepository.save method
Room bedroom = new Room(home);
bedroom = locationService.save(bedroom);
I get a duplicate entry of room in the database which I think is related to a Hibernate issue https://hibernate.onjira.com/browse/HHH-7404. If I just call
Home home = new Home();
Room bedroom = new Room(home);
locationService.save(home)
there are no doubles but I have no way to retrieve the room object since it was persisted on cascade and its id is 0. Is there a way to solve this without introducing other fields in the location like a unique name that I have to generate myself? Any help is much appreciated.
Edit
If in the last case I have home = locationService.save(home) and then call home.getUuid() I get the right value which is normal I think since I retrieve a managed object. But if I do bedroom.getUuid() I get 0 since bedroom is not managed and so it has not had its id field updated with the value from the database.
Have you tried calling home.getUuid(); (assuming you have a getter for that field) after the persist call?
You might be surprised, but Hibernate (and JPA) will update the in memory copy with the id.

Resources