How to save ManyToOne realtion by ID in Spring Boot JPA - spring-boot

My application contains one class that looks like this:
import javax.persistence.*;
#Entity
public class PerformanceIndicator {
#Id #GeneratedValue private Long id;
private String name;
private String description;
private int level;
/**
* #JoinColumn says that Performance Indicator table will contain a separate column
* ARCHITECTURAL_LAYER_ID which will eventually act as a foreign key reference to primary key of
* Accountability Model table. #ManyToOne says that multiple Architectural Layers can refer to
* same Accountability Model (Multiple Architectural Layers can be registered in same
* Accountability Model). Additionally , with optional=false we make sure that Architectural Layer
* can exist without a Accountability Model.
*/
#ManyToOne()
#JoinColumn(name = "activity_id")
private Activity activity;
#ManyToOne()
#JoinColumn(name = "architectural_layer_id")
private ArchitecturalLayer architecturalLayer;
public PerformanceIndicator() {}
public PerformanceIndicator(String name, String description, int level) {
this.name = name;
this.description = description;
this.level = level;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public int getLevel() {
return level;
}
public void setLevel(int level) {
this.level = level;
}
public Activity getActivity() {
return activity;
}
public void setActivity(Activity activity) {
this.activity = activity;
}
public ArchitecturalLayer getArchitecturalLayer() {
return architecturalLayer;
}
public void setArchitecturalLayer(ArchitecturalLayer architecturalLayer) {
this.architecturalLayer = architecturalLayer;
}
#Override
public String toString() {
return "PerformanceIndicator{"
+ "id="
+ id
+ ", name='"
+ name
+ '\''
+ ", description='"
+ description
+ '\''
+ ", level="
+ level
+ ", activity="
+ activity
+ ", architecturalLayer="
+ architecturalLayer
+ '}';
}
}
And architectural layer class:
import com.fasterxml.jackson.annotation.JsonBackReference;
import javax.persistence.*;
#Entity
public class ArchitecturalLayer {
#Id #GeneratedValue private Long id;
private String name;
private String description;
/**
* #JoinColumn says that Architectural Layer table will contain a separate column
* ARCHITECTURAL_LAYER_ID which will eventually act as a foreign key reference to primary key of
* Accountability Model table. #ManyToOne says that multiple Architectural Layers can refer to
* same Accountability Model (Multiple Architectural Layers can be registered in same
* Accountability Model). Additionally , with optional=false we make sure that Architectural Layer
* can exist without a Accountability Model.
*/
#ManyToOne
#JoinColumn(name = "accountablility_model_id")
#JsonBackReference
private AccountablityModel accountablityModel;
public ArchitecturalLayer() {}
public ArchitecturalLayer(Long id, String name, String description) {
this.id = id;
this.name = name;
this.description = description;
}
public ArchitecturalLayer(String name, String description) {
this.name = name;
this.description = description;
}
public ArchitecturalLayer(Long id) {
this.id = id;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public AccountablityModel getAccountablityModel() {
return accountablityModel;
}
public void setAccountablityModel(AccountablityModel accountablityModel) {
this.accountablityModel = accountablityModel;
}
#Override
public String toString() {
return "ArchitecturalLayer{"
+ "id="
+ id
+ ", name='"
+ name
+ '\''
+ ", description='"
+ description
+ '\''
+ ", accountablityModel="
+ accountablityModel
+ '}';
}
}
When I try to save a PerformanceIndicator object that already has a existing I want to do this by it's ID. But there also have to be an option to create an Activity and Architectural layer if the users wants to.
for example:
PerformanceIndicator p = new PeformanceIndicator();
p.setName("test");
p.setDescription("test");
p.setLevel(1);
p.setActivity(1);
p.setArchitecturalLayer(1);
performanceIndicatorService.save(p);
What can I do to be able to persist this by passing an ID but also be able to pass in a object of the Activity/ArchitecturalLayer?

The objects of PerformanceIndicator class require Activity/ArchitectualLayer objects. If you only have the identifiers of those objects, you can obtain references to those objects by calling the getOne method from a corresponding repository.
The getOne method does not load an object from a database, but creates a proxy/reference object (under the hood it calls the getReference method of EntityManager). It's recommended exactly in such scenarios where you have an id and only want to reference an object.
So, your code could look like:
PerformanceIndicator p = new PeformanceIndicator();
p.setName("test");
p.setDescription("test");
p.setLevel(1);
p.setActivity(activityRepository.getOne(1));
p.setArchitecturalLayer(architecturalLayerRepository.getOne(1));
performanceIndicatorRepository.save(p);
Usually, such code is inside a service method (like save/create) which is responsible for 'filling gaps' and performing all extra work to properly save an object.

Related

How can I fetch data from 2 tables using HQL having one to many association with each other?

I have 2 tables in my database, city and hotel_details. Primary key of city is foreign key in hotel_details and associated with one to many association. I want to fetch data(status,registration,etc..) from hotel_details based on city_id and hotel_name by calling getAvailabilityStatus from my controller. Following is my code :
City Entity class
#Entity
#Table(name="city")
public class City {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="id")
private int id;
#Column(name="city_name")
private String cityName;
#OneToMany(mappedBy="city",
cascade= {CascadeType.PERSIST, CascadeType.MERGE,
CascadeType.DETACH, CascadeType.REFRESH})
private List<HotelDetails> hotelDetails;
public City() {
}
public List<HotelDetails> getHotelDetails() {
return hotelDetails;
}
public void setHotelDetails(List<HotelDetails> hotelDetails) {
this.hotelDetails = hotelDetails;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getCityName() {
return cityName;
}
public void setCityName(String cityName) {
this.cityName = cityName;
}
#Override
public String toString() {
return "City [id=" + id + ", cityName=" + cityName + "]";
}
}
2.HotelDetails Entity class
#Entity
#Table(name="hotel_details")
public class HotelDetails {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="id")
private int id;
#ManyToOne(cascade= {CascadeType.PERSIST, CascadeType.MERGE,
CascadeType.DETACH, CascadeType.REFRESH})
#JoinColumn(name="city_id")
private City city;
#Column(name="hotel_name")
private String hotelName;
#Column(name="available_date")
#DateTimeFormat(pattern = "dd/MM/yyyy")
private Date availableDate;
#Column(name="price")
private int price;
#Column(name="gst")
private int gst;
#Column(name="status")
private int status;
#Column(name="room_type")
private String roomType;
public HotelDetails() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public City getCity() {
return city;
}
public void setCity(City city) {
this.city = city;
}
public String getHotelName() {
return hotelName;
}
public void setHotelName(String hotelName) {
this.hotelName = hotelName;
}
public Date getAvailableDate() {
return availableDate;
}
public void setAvailableDate(Date availableDate) {
this.availableDate = availableDate;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public int getGst() {
return gst;
}
public void setGst(int gst) {
this.gst = gst;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public String getRoomType() {
return roomType;
}
public void setRoomType(String roomType) {
this.roomType = roomType;
}
#Override
public String toString() {
return "HotelDetails [id=" + id + ", hotelName=" + hotelName + ", availableDate=" + availableDate + ", price="
+ price + ", gst=" + gst + ", status=" + status + ", roomType=" + roomType + "]";
}
}
3.HotelDAOImpl
#Component
#Repository
public class HotelDetailsDAOImpl implements HotelDetailsDAO {
#Autowired
private SessionFactory sessionFactory;
#Override
#Transactional
public Set<String> getHotels() {
Session currentSession = sessionFactory.getCurrentSession();
Query theQuery2 = currentSession.createQuery("Select h.hotelName from HotelDetails h");
List<String> listHotels = theQuery2.list();
Set<String> hotels = new HashSet<String>(listHotels);
return hotels;
}
#Override
#Transactional
public List<City> getAvailabilityStatus(int cityID, String hotelName, String cityName) {
Session currentSession = sessionFactory.getCurrentSession();
Query theQuery4 = currentSession.createQuery("...");
//theQuery4.setParameter("hotelName", hotelName);
//List<City> cities = theQuery4.list();
return cities;
}
}
String jpql = "select c from City c join c.hotelDetails h where h.hotelName = :hotelName";
or
String jpql = "select c from HotelDetails h join h.city c where h.hotelName = :hotelName";
and then
Query theQuery4 = currentSession.createQuery(jpql);
theQuery4.setParameter("hotelName", hotelName);
List<City> cities = theQuery4.list();
This is just an example, but once you have defined the join and the entity aliases correctly, you can refer to entity attributes in the WHERE clause any way you prefer.
For example:
jpql += " AND c.id=:id AND h.price<:price AND h.availableDate BETWEEN :start AND :end";
same for the select clause, you can use all the combinations of:
"select c.cityName, h.status, ..."
"select c, h from ..."
Check the Hibernate ORM query for many examples of how you can use JPQL/HQL.

createalias in hibernate is joining parent table two times

I have bidirectional one to many between department and employees.ie one department can have multiple employees.
I want to join department and employees using hibernate criteria so for that i am using createalias method.
Criteria criteriaDepartment = session.createCriteria(DepartmentEntity.class);
criteriaDepartment.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);
criteriaDepartment.createAlias("employeeEntity", "emp",JoinType.LEFT_OUTER_JOIN);
List<DepartmentEntity> list = criteriaDepartment.list();
However, hibernate joins department table with employees and then again with the department table.
Query generated by hibernate is as follows:
Hibernate: select this_.id as id1_1_2_, this_.name as name2_1_2_, emp1_.deptId as deptId7_2_4_, emp1_.id as id1_2_4_, emp1_.id as id1_2_0_, emp1_.address as address2_2_0_, emp1_.deptId as deptId7_2_0_, emp1_.password as password3_2_0_, emp1_.phoneno as phoneno4_2_0_, emp1_.type as type5_2_0_, emp1_.userid as userid6_2_0_, department4_.id as id1_1_1_, department4_.name as name2_1_1_ from Department4 this_ left outer join Userdetails4 emp1_ on this_.id=emp1_.deptId left outer join Department4 department4_ on emp1_.deptId=department4_.id
Why Department table is joining multiple times??? Is there any way to prevent this. I have to use some restrictions as well.
However when i use fetchmode it works fine but with this method i am not able to use aliases.
Department class:
#Entity
#Table(name = "Department4")
public class DepartmentEntity {
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE)
public int id;
public String name;
#OneToMany(mappedBy= "departmentEntity", cascade= CascadeType.ALL, orphanRemoval = true)
public Set<EmployeeEntity> employeeEntity = new HashSet<EmployeeEntity>();
public Set<EmployeeEntity> getEmployeeEntity() {
return employeeEntity;
}
public void setEmployeeEntity(Set<EmployeeEntity> employeeEntity) {
this.employeeEntity = employeeEntity;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public String toString() {
return "DepartmentEntity [id=" + id + ", name=" + name + ", employeeEntity=" + employeeEntity + "]";
}
}
Employee class
#Entity
#Table(name="Userdetails4")
public class EmployeeEntity {
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE)
private int id;
private String userid;
private String password;
private String address;
private long phoneno;
private String type;
#ManyToOne
#JoinColumn(name = "deptId")
private DepartmentEntity departmentEntity;
#OneToMany(mappedBy = "employeeEntity", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<AddressEntity> addressEntity = new HashSet<AddressEntity>();
public Set<AddressEntity> getAddressEntity() {
return addressEntity;
}
public void setAddressEntity(Set<AddressEntity> addressEntity) {
this.addressEntity = addressEntity;
}
public DepartmentEntity getDepartmentEntity() {
return departmentEntity;
}
public void setDepartmentEntity(DepartmentEntity departmentEntity) {
this.departmentEntity = departmentEntity;
}
public EmployeeEntity()
{}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUserid() {
return userid;
}
public void setUserid(String userid) {
this.userid = userid;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public long getPhoneno() {
return phoneno;
}
public void setPhoneno(long phoneno) {
this.phoneno = phoneno;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
#Override
public String toString() {
return "EmployeeEntity [id=" + id + ", userid=" + userid + ", password=" + password + ", address=" + address
+ ", phoneno=" + phoneno + ", type=" + type + "]";
}
}
Hibernate version: 5.2.1
Thanks in advance.

Spring JPARepository Update a field

I have a simple Model in Java called Member with fields - ID (Primary Key), Name (String), Position (String)
I want to expose an POST endpoint to update fields of a member. This method can accept payload like this
{ "id":1,"name":"Prateek"}
or
{ "id":1,"position":"Head of HR"}
and based on the payload received, I update only that particular field. How can I achieve that with JPARepository?
My repository interface is basic -
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
#Repository("memberRepository")
public interface MemberRepository extends JpaRepository<Member, Integer>{
}
My Member model -
#Entity
#Table(name="members")
public class Member {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="member_id")
private Integer id;
#Column(name="member_name")
#NotNull
private String name;
#Column(name="member_joining_date")
#NotNull
private Date joiningDate = new Date();
#Enumerated(EnumType.STRING)
#Column(name="member_type",columnDefinition="varchar(255) default 'ORDINARY_MEMBER'")
private MemberType memberType = MemberType.ORDINARY_MEMBER;
public Member(Integer id, String name, Date joiningDate) {
super();
this.id = id;
this.name = name;
this.joiningDate = joiningDate;
this.memberType = MemberType.ORDINARY_MEMBER;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getJoiningDate() {
return joiningDate;
}
public void setJoiningDate(Date joiningDate) {
this.joiningDate = joiningDate;
}
public MemberType getMemberType() {
return memberType;
}
public void setMemberType(MemberType memberType) {
this.memberType = memberType;
}
public Member(String name) {
this.memberType = MemberType.ORDINARY_MEMBER;
this.joiningDate = new Date();
this.name = name;
}
public Member() {
}
}
Something like this should do the trick
public class MemberService {
#Autowired
MemberRepository memberRepository;
public Member updateMember(Member memberFromRest) {
Member memberFromDb = memberRepository.findById(memberFromRest.getid());
//check if memberFromRest has name or position and update that to memberFromDb
memberRepository.save(memberFromDb);
}
}

How to update values associated with Primary Key in Spring-JPA

I want to update a record associated with a primary key using Spring-JPA.
GroupChatHeartBeat groupChatHeartBeat=new GroupChatHeartBeat();
groupChatHeartBeat.setId(user.getId());
groupChatHeartBeat.setGender(user.getGender());
groupChatHeartBeat.setHeartBeatTime(new Date());
groupChatHeartBeat.setUrl(userPhoto.getSrcBig());
groupChatHeartBeatRepository.save(groupChatHeartBeat);
where GroupChatHeartBeat is declared as Entity, but doing so it's not replacing with new value. it is showing old value only. My intention is to update the table and if an id exist it should get replaced with new records(like time, gender, url etc ). here is the entity
#Entity
#Table
public class GroupChatHeartBeat implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private Long id;
private Date heartBeatTime;
private String url;
private Gender gender;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public Date getHeartBeatTime() {
return heartBeatTime;
}
public void setHeartBeatTime(Date heartBeatTime) {
this.heartBeatTime = heartBeatTime;
}
public Gender getGender() {
return gender;
}
public void setGender(Gender gender) {
this.gender = gender;
}
}
Try this :
To update an existing entity in database - You must set the Object Id of new Object to OldObject Id.
i.e. newObject.setId(OldObject.getId()) and then repo.save(newObject) will update the existing entity in the database.
Entity Class
#Entity
public class GroupChatHeartBeat {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Date heartBeatTime;
private String url;
public GroupChatHeartBeat() {
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Date getHeartBeatTime() {
return heartBeatTime;
}
public void setHeartBeatTime(Date heartBeatTime) {
this.heartBeatTime = heartBeatTime;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public GroupChatHeartBeat(Date heartBeatTime, String url) {
this.heartBeatTime = heartBeatTime;
this.url = url;
}
#Override
public String toString() {
return "GroupChatHeartBeat{" +
"id=" + id +
", heartBeatTime=" + heartBeatTime +
", url='" + url + '\'' +
'}';
}
}
Autowire Repository
#Autowired
private GroupChatHeartBeatRepository groupChatHeartBeatRepository;
Save and Update
// Save New
GroupChatHeartBeat grp = new GroupChatHeartBeat(new Date(), "http://www.google.com");
groupChatHeartBeatRepository.save(grp);
groupChatHeartBeatRepository.findAll().forEach(System.out::println);
// Update same object and save-> updates value of existing in database
grp.setUrl("http://spring.io");
groupChatHeartBeatRepository.save(grp);
groupChatHeartBeatRepository.findAll().forEach(System.out::println);
// Create New object, set Id of new object as old object and save-> updates value of existing in database
GroupChatHeartBeat grpUpdated = new GroupChatHeartBeat(new Date(638893800000L), "https://github.com/RawSanj");
grpUpdated.setId(grp.getId());
groupChatHeartBeatRepository.save(grpUpdated);
groupChatHeartBeatRepository.findAll().forEach(System.out::println);
Checkout the Complete Project in my GitHub repository.

Spring Data Neo4j 4 - findById(Long id) return null

I am using SDN 4 and neo4j-ogm 1.1.4
I am trying to fetch my data using findById(Long id) GraphRepository, but always return null. After that I am trying to use findByName(String name) and its worked. I know there is alternative using findOne(Long id, int depth), but when I want to make custom query, for example findByObjectId(Long id), it will be trouble.
After try manual query at neo4j, it return null too. So any issue about this ?
#NodeEntity
public class Fetch1 {
#GraphId Long id;
private String name;
#Relationship(type="HAS_FETCH2")
#JsonIgnore
private List<Fetch2> fetch2;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Relationship(type="HAS_FETCH2")
#JsonIgnore
public List<Fetch2> getFetch2() {
return fetch2;
}
#Relationship(type="HAS_FETCH2")
#JsonIgnore
public void setFetch2(List<Fetch2> fetch2) {
this.fetch2 = fetch2;
}
#Override
public String toString() {
return "Fetch1 [id=" + id + ", name=" + name + ", fetch2=" + fetch2 + "]";
}
}
#NodeEntity
public class Fetch2 {
#GraphId Long id;
private String name;
#Relationship(type="HAS_FETCH2", direction=Relationship.INCOMING)
#JsonIgnore
private Fetch1 fetch1;
#Relationship(type="HAS_FETCH3")
#JsonIgnore
private List<Fetch3> fetch3;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Relationship(type="HAS_FETCH2", direction=Relationship.INCOMING)
#JsonIgnore
public Fetch1 getFetch1() {
return fetch1;
}
#Relationship(type="HAS_FETCH2", direction=Relationship.INCOMING)
#JsonIgnore
public void setFetch1(Fetch1 fetch1) {
this.fetch1 = fetch1;
}
#Relationship(type="HAS_FETCH3")
#JsonIgnore
public List<Fetch3> getFetch3() {
return fetch3;
}
#Relationship(type="HAS_FETCH3")
#JsonIgnore
public void setFetch3(List<Fetch3> fetch3) {
this.fetch3 = fetch3;
}
#Override
public String toString() {
return "Fetch2 [id=" + id + ", name=" + name + ", fetch1=" + fetch1 + ", fetch3=" + fetch3 + "]";
}
}
And this is my Repository
public interface Fetch1Repository extends GraphRepository<Fetch1>{
Fetch1 findById(Long id);
Fetch1 findByFetch2Id(Long id);
Fetch1 findByFetch2Name(String name);
}
In this case, findById won't work the way you expect, because the id is not a node property in the graph, and findByXXX looks for properties.
In Cypher, the difference is:
MATCH (n) WHERE id(n) = .... // find by id
MATCH (n {n.name = "Steve Jobs" }) ... // find by property
Just use findOne(id) or findOne(id, depth).

Resources