COUNTRY and ADDRESS table has one-to-many relationship and ADDRESS and COUNTRY has many-to-one relationship. COUNTRY table has Id and Country Names. Address table has Country(has country codes from COUNTRY table) and NAME.
NAME and COUNTRY are unique keys in the ADDRESS table.
How to add Address Entity's NAME to CountryAddress Embeddable class? ( embeddable attributes are from different entities)
#Entity
public class Country {
#Column(name = "COUNTRY")
private String countryName;
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "country")
public Map<CountryAddress, Address> getAddressMap() {
return addressMap;
}
#Embeddable
public class CountryAddress {
#Column(name = "COUNTRY")
private String countryName;
// Need to add NAME from Address
}
}
#Entity
public class Address {
private Integer id;
#Column(name = "NAME")
private String name;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "COUNTRY")
private Country country;
}
Related
How to specify the children's property name while creating the PageRequest object. In the below example, I want to sort based on the name property of the Customer entity which is part of the Order class. I should be able to sort on any of the parent or child field.
Sort.Direction sortDirection = direction
.equalsIgnoreCase("asc") ? Sort.Direction.ASC : Sort.Direction.DESC;
//how to make the `name` property map to `Customer` name
PageRequest pageRequest = PageRequest.of(page, size, sortDirection, "name");
Page<Order> pageResponse = this.orderRepository.findAll(pageRequest);
#Entity
#Table(name = "orders")
public class Order {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToOne
#JoinColumn(name="customer_id", nullable = false)
#JsonBackReference
private Customer customer;
...
----------
#Entity
#Table(name = "customers")
#NoArgsConstructor
public class Customer implements UserDetails {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#NotEmpty(message = "customer name cannot be empty")
private String name;
#OneToMany(mappedBy = "customer", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JsonManagedReference
private Set<Order> orders;
...
I have 3 entities named Student, Course, and StudentCourse as follows
#Entity
#Table(name = "student")
public class Student {
#Id
#GeneratedValue
private Integer id;
private String fullName;
}
#Entity
#Table(name = "course")
public class Course {
#Id
#GeneratedValue
private Integer id;
private String courseName;
}
#Entity
#Table(name = "student_course")
public class StudeCourse {
#Id
#GeneratedValue
private Integer studentId;
private Integer courseId;
private String extraColumn;
}
Restrictions: There are a couple of restrictions
One student can have only one course or no course at all
An extra entity (StudentCourse) is required to hold the relation with primary key as studentId only
StudentCourse is required and hence cannot be skipped
Get Student with Course entity if there is one registered
Help required in some magical code to retrieve Course of Student if there is one assigned.
#Entity
#Table(name = "student")
public class Student {
#Id
#GeneratedValue
private Integer id;
private String fullName;
// this is not correct code but just what I want
#JoinEntity(entity=StudentCourse, column="courseId")
private Course course;
}
StudentCourse is required and hence cannot be skipped
Ok, lets work with that.
One student can have only one course or no course at all
Implies that there is a #OneToOne relationship between Student and StudentCourse.
With the given information, the following entity model will work:
#Entity
#Table(name = "student")
public class Student {
#Column(name = "id")
#Id
#GeneratedValue
private Integer id;
#Column(name = "full_name")
private String full_name;
#OneToOne
#PrimaryKeyJoinColumn
private StudentCourse studentCourse;
}
#Entity
#Table(name = "student_course")
public class StudentCourse {
#Column(name = "id")
#Id
#GeneratedValue
private Integer id;
#JoinColumn(name = "id", nullable = false, updatable = false)
#MapsId
#OneToOne
private Student student;
#JoinColumn(name = "course_id")
#ManyToOne
private Course course;
...
}
A quick review:
#OneToOne on the Student.studentCourse field signifies that for every Student, there can be only one StudentCourse, and no more.
#PrimaryKeyJoinColumn on the Student.studentCourse field signifies that the value of the primary key column for Student should be used as the foreign key for the related entity, that is, StudentCourse.
#OneToOne on the StudentCourse.student field signifies that for every StudentCourse, there can be only one Student.
#MapsId on the StudentCourse.student field signifies that the primary key column for StudentCourse should be used as the join column for the association.
To check if a student has a course assigned, simply check if student.getStudentCourse() != null and then get the assigned course as student.getStudentCourse().getCourse().
How to give composite unique keys ( address1 and address2) as Map key. Like #MapKey(name = "address1", name = "address2") instead of single unique key #MapKey(name = "address1").
#Entity
public class Person {
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "person")
#MapKey(name = "address1")
public Map<String, Address> getAddressMap() {
return addressMap;
}
}
#Entity
public class Address {
private Integer id;
private String address1;
private String address2;
private Person person;
}
Use Embeddable type.
Create an embeddable class (i.e. PersonAddress) to encapsulate your address1 and address2 properties. Then use that embeddable class as a Map key.
#Entity
public class Person {
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "person")
public Map<PersonAddress, Address> getAddressMap() {
return addressMap;
}
}
#Entity
public class Address {
private Integer id;
#Embedded
private PersonAddress;
#ManyToOne
private Person person;
}
#Embeddable
public class PersonAddress {
private String address1;
private String address2;
}
I've 2 entities:
#Entity
public class Customer{
#Id
#Column(name = "ID")
private Long id;
#OneToOne(mappedBy = "customer")
private Address address;
#Column(name = "FIELD_1")
private String field1;
#Column(name = "FIELD_2")
private String field2;
}
#Entity
public class Address{
#Id
#Column(name = "ID")
private Long id;
#OneToOne
#JoinColumn(name = "CUST_ID")
private Customer customer;
}
If I do a select to retrieve the Customer, the Adress is retrieved as well, all good.
But I want to do a projection and build a DTO, so only select field1 and field2 along with the Adress entity.
public class MyDTO {
private String field1;
private String field2;
private Address address;
public MyDTO (String pField1, String pField2, Adress pAddress){
field1 = pField1;
field2 = pField2;
adress = pAddress;
}
}
So I've coded that DAO method :
public List<MyDTO > getListMyDTO() {
CriteriaQuery<MyDTO > crit = builder.createQuery(MyDTO .class);
Root<Customer> root = crit.from(Customer.class);
// This is to avoid a inner join since some customer may not have an adress and i dont want to exclude them from my select
root.join("address", JoinType.LEFT);
crit.multiselect(root.get("field1"), root.get("field2"), root.get("address"));
return em.createQuery(crit).getResultList();
}
em being the entity manager.
However that doesn't work and the address ends up always null.
I sorta understand this as there is no field from root pointing to the address table, but since it works when i do a select on the entity instead of a DTO, there must be a way to make this work no?
I am using EJB3.2 and JPA 2.1.
In my application I have these entities (in brief) :
#Entity
public class Festival
{
int Id;
String name;
...
#ManyToOne
#JoinColumn(name = "merchant", referencedColumnName = "Id")
Merchant merchant;
}
#Entity
public class Merchant
{
int Id;
String name;
...
#ManyToOne
#JoinColumn(name = "category", referencedColumnName = "Id")
Category category;
#ManyToOne
#JoinColumn(name = "city", referencedColumnName = "Id")
City city;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "festival")
List<Festival> festivalList;
}
#Entity
public class Category
{
int Id;
String name;
...
#OneToMany(cascade = CascadeType.ALL, mappedBy = "merchant")
List<Merchant> merchantList;
}
#Entity
public class City
{
int Id;
String name;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "merchant")
List<Merchant> merchantList;
}
thus, (Festival, Merchant) has a bidirectional relationship like (Merchant, Category) and (Merchant, City)
the problem is when i remove or change a merchant, still there is an unchanged copy of it in categories merchantList and cities merchantList ! and so on..
how should I manage these changes?! why JPA doesn't do the changes to other copies it self? doesn't it increase the wrong copies risk?!
JPA treats your entities like regular java objects and does not maintain relationships for you - the application is responsible for ensuring that both sides of a bidirectional relationship are kept in sync when you change one side. So when you dereference a City from a Merchant, you must remove the reference to the Merchant from the City's merchantList and then merge if necessary.
Otherwise your view of the data via the java objects will become out of synch with the database until the objects are refreshed from the database. You can weigh the value and costs of keeping both sides in sync or refreshing when needed or even not mapping the non-owning side and determine what is better for your application on an entity by entity basis.
Make sure to persist(merchant) before calling categories.getMerchantList().
I think this quote from "Java Persistence With Hibernate" (p. 261) by Christian Bauer and Gavin King may be useful for you :
Contrary to EJB 2.0 CMR, Hibernate and JPA associations are all
inherently unidirectional.
So the association from Merchant to Category is different than association from Category to Merchant, and you need to manage them separately.
edit:
I adjusted your initial bean config to look as it should:
(Of course, you will use an appropriate primary key generation strategy.)
#Entity
public class Festival
{
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
int Id;
String name;
#ManyToOne(cascade=CascadeType.ALL)
#JoinColumn(name = "merchant")
Merchant merchant;
}
#Entity
public class Merchant
{
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
int Id;
String name;
#ManyToOne
#JoinColumn(name = "category")
Category category;
#ManyToOne
#JoinColumn(name = "city")
City city;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "merchant")
List<Festival> festivalList;
}
#Entity
public class Category
{
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
int Id;
String name;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "category")
List<Merchant> merchantList;
}
#Entity
public class City
{
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
int Id;
String name;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "city")
List<Merchant> merchantList;
}