I have 3 tables. 1 is parent, 1 is student and one is student_parent. This is a many to many relationship between the parent and student. The parentID is from the parent table and the studentID is from the student table. The student_parent table have studentID, parentID and childRelationship. I need to know how to update the childRelationship field using JPA(hibernate).
Note that I'm using Java Spring with the JPA/Hibernate to update my relational database and that there are several other tables but all I wanted to know was to update childRelationship field.
#Entity
#Table(name = "Student")
#Proxy(lazy = false)
public class Student{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "studentID", nullable = false)
private int studentID;
#Column(name="firstName")
private String firstName;
#Column(name="middleName")
private String middleName;
#Column(name="lastName")
private String lastName;
#Column(name="validationID")
private String validationID;
#Column(name="birthDate")
private Date birthDate;
#Column(name="gender")
private char gender;
#Column(name="ethnicity")
private String ethnicity;
#Column(name="photo") // character type has to confirmed
private byte[] photo;
#Column(name="gradeLevel")
private int gradeLevel;
#Column(name="rewardPoints")
private String rewardPoints;
#Column(name="dateCreated")
private Date dateCreated;
#Column(name="schoolID")
private Integer schoolID;
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "addressID")
private Address address;
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "userName")
private UserAccount userAccount;
#ManyToMany(cascade = {CascadeType.ALL}, fetch = FetchType.EAGER)
#JoinTable(name="student_parent",
joinColumns = {#JoinColumn(name = "studentID")},
inverseJoinColumns = {#JoinColumn(name = "parentID")})
private Set<Parent> parents = new HashSet<Parent>();
#ManyToMany(cascade = {CascadeType.ALL}, fetch = FetchType.EAGER)
#JoinTable(name="student_community",
joinColumns = {#JoinColumn(name = "studentID")},
inverseJoinColumns = {#JoinColumn(name = "communityID")})
private Set<Community> communities = new HashSet<Community>();
//getter and setting
then the parent.
#Entity
#Proxy(lazy = false)
public class Parent {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int parentID;
#Column(name="userName")
private String userName;
#Column(name="firstName")
private String firstName;
#Column(name="middleName")
private String middleName;
#Column(name="lastName")
private String lastName;
#Column(name="validationID")
private String validationID;
#Column(name="maritalStatus")
private String maritalStatus;
#Column(name="noOfChildren")
private int noOfChildren;
#Column(name="birthDate")
private Date birthDate;
#Column(name="gender")
private char gender;
#Column(name="ethnicity")
private String ethnicity;
#Column(name="annualIncome")
private Float annualIncome;
#Column(name="dateCreated")
private Date dateCreated;
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "addressID")
private Address address;
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "userName",unique = true, nullable = false, insertable=false, updatable=false)
private UserAccount userAccount;
//getter and setter
You already have an association table, you need to bring it into the entity world as an association class. The primary key of the association class is a composite key made of the student id and the parent id. Don't forget to implement hashcode and equals on the primary key class. With this mapping you can gain access to the attributes of the m:n relationships (i.e. the "childRelationship" column). You'll also need to add getters and setters or go with field access.
/**
* Maps to parent table in database
*/
#Entity
class Parent {
#Id
long id;
#OneToMany(mappedBy="parent")
Set<Relationship> relationships;
}
/**
* Maps to student table in database
*/
#Entity
class Student {
#Id
long id;
#OneToMany(mappedBy="student")
Set<Relationship> relationships;
}
/**
* Encapsulates primary key in relationship table
*/
class RelationshipId {
long studentId;
long parentId;
#Override
public int hashCode() {
return ((Long)(studentId + parentId)).hashCode();
}
#Override
public boolean equals(Object obj) {
return obj != null
&& obj instanceof RelationshipId
&& ((RelationshipId)obj).studentId == studentId
&& ((RelationshipId)obj).parentId == parentId;
}
}
/**
* Maps to relationship table in database
*/
#Entity
#IdClass(RelationshipId.class)
class Relationship {
#Id
#ManyToOne(optional = false)
#JoinColumn(name="parent")
Parent parent;
#Id
#ManyToOne(optional = false)
#JoinColumn(name="student")
Student student;
#Column
String childRelationship;
}
Related
I have two entities in java (Student and Group) with a #OneToMany relationship
public class Group {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Column(name="name")
private Integer groupName;
public Group(Integer groupName) {
this.groupName = groupName;
}
#OneToMany
#JoinTable(name = "groups_students",
joinColumns= #JoinColumn(name = "group_id"),
inverseJoinColumns = #JoinColumn(name = "student_id")
)
private Set<Student> students;
}
And I have a third column date in the table groups_students on the localhost. date is not a field in classes. I need to get this in the #Query. Can anyone please help how can I get this (maybe give information to the code about this date column or any other ways to)
public class Student {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Column(name="first_name")
private String firstName;
#Column(name="last_name")
private String lastName;
public Student(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
}
The way of handling additional attributes from the join table is to define that as a separate entity and then split the relation using the intermediate entity
#Entity
#Table(name = "groups_students")
public class GroupStudent {
#Id
#ManyToOne(fetch = FetchType.LAZY)
private Group group;
#Id
#ManyToOne(fetch = FetchType.LAZY)
private Student student;
#Column(name = "date")
private Date date;
//getter-setter
}
Now modify the relationship on the Group entity
#OneToMany(mappedBy = "group", cascade = CascadeType.ALL)
private List<GroupStudent> groupStudents = new ArrayList<>();
And modify also the Student entity relation
#OneToMany(mappedBy = "student", cascade = CascadeType.ALL)
private List<GroupStudent> groupStudents = new ArrayList<>();
Now if you want to query the additional attribute like the date, you can have a JPQL like this (for example, if you want to get all students of a group where date is in the past)
#Query(SELECT s FROM Student s JOIN s.groupStudents gs WHERE gs.group = :group and gs.date < CURRENT_DATE)
If i do this;
public class Client{
#Id
#Column(columnDefinition = "CHAR(11)")
private String cpf;
#ManyToMany(cascade = CascadeType.ALL)
private List<Address> addresses;
//get set
}
.
public class Address{
#Id
private String zipCode;
#Id
private String number;
#Id
#Column(columnDefinition = "varchar(255) default 'DONT HAVE'")
private String complement;
//get set
}
... I have this mapping:
Image - Model using java.util.List with #ManyToMany
If i do this;
public class Client{
#Id
#Column(columnDefinition = "CHAR(11)")
private String cpf;
#ManyToMany(cascade = CascadeType.ALL)
private Set<Address> addresses;
//get set
}
.
public class Address{
#Id
private String zipCode;
#Id
private String number;
#Id
#Column(columnDefinition = "varchar(255) default 'DONT HAVE'")
private String complement;
//get set
}
... I have this mapping:
Image - Model using java.util.Set with #ManyToMany
The Question is:
how do I get the attributes of the extra table automatically generated by #ManyToMany relationship are primary foreign keys(PFK) using java.util.List?
in my jpa study, we don't need create a primary key in extra table generate by #ManyToMany.
This is my final solution:
public class Client{
#Id
#Column(columnDefinition = "CHAR(11)")
private String cpf;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name="client_address", joinColumns = {
#JoinColumn(name = "client_cpf") },
inverseJoinColumns = { #JoinColumn(name = "address_zipcode",
referencedColumnName="zipCode"), #JoinColumn(name = "address_number",
referencedColumnName="number", #JoinColumn(name =
"address_complement",
referencedColumnName="complement"},
uniqueConstraints = #UniqueConstraint(columnNames = {
"client_cpf", "address_zipcode", "address_number",
"address_complement" })
private List<Address> addresses;
//get set
}
I have 3 data table:
Applications {id_app, version, name}
Customers {org_id, name}
Associations {id_app, version, org_id, state}.
Applications has a composite primary key (id_app, version), the primary key for Customers is org_id and Associations has a composite primary key (id_app, version, org_id).
In my java application I have the following classes:
#Entity
#Table(name = "Applications")
#IdClass(ApplicationId.class)
public class Application implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "ID_APP", nullable = false)
private String idApp;
#Id
#Column(name = "VERSION", nullable = false)
private String version;
#Column(name = "NAME")
private String name;
#OneToMany(mappedBy = "idPk.appPk", fetch = FetchType.LAZY) // cascade = CascadeType.ALL)
private List<Association> custApps;
// getters and setters
}
public class ApplicationId implements Serializable {
private static final long serialVersionUID = 1L;
private String idApp;
private String version;
//hashcode and equals
}
#Entity
#Table(name = "CUSTOMERS")
public class Customer implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "ORG_ID", unique = true, nullable = false)
private Integer orgID;
#Column(name = "NAME")
private String name;
#OneToMany(mappedBy="idPk.customerPk", fetch = FetchType.LAZY)
private List<Association> custApps;
//getters and setters
}
#Entity
#Table(name = "ASSOCIATIONS")
#AssociationOverrides({
#AssociationOverride(name = "idPk.appPk", joinColumns = #JoinColumn(name = "ID_APP")),
#AssociationOverride(name = "idPk.appPk", joinColumns = #JoinColumn(name = "VERSION")),
#AssociationOverride(name = "idPK.customerPk", joinColumns = #JoinColumn(name = "ORG_ID"))
})
public class Association implements Serializable {
private static final long serialVersionUID = 1L;
private AssociationId idPk = new AssociationId();
private String state;
public Association() {
super();
}
#EmbeddedId
public AssociationId getIdPk() {
return idPk;
}
#Transient
public Customer getCustomerPk() {
return idPk.getCustomerPk();
}
#Transient
public Application getAppPk() {
return idPk.getAppPk();
}
#Column(name = "STATE")
public String getState() {
return state;
}
//setters , hashCode and equals
}
#Embeddable
public class AssociationId implements Serializable {
private static final long serialVersionUID = 1L;
private Application appPk;
private Customer customerPk;
// here is the problem ?
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumns({ #JoinColumn(name = "ID_APP", referencedColumnName = "ID_APP"),
#JoinColumn(name = "VERSION", referencedColumnName = "VERSION") })
public Application getAppPk() {
return appPk;
}
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name="ORG_ID")
public Customer getCustomerPk() {
return customerPk;
}
//setter, hashCode and equals
}
What are the correct annotation? The relationship is many to many between Application and Customers and I create the Association table for that and for the extra column "state".
Now I receive this error: A Foreign key refering sla.model.Application from sla.model.Association has the wrong number of column. should be 2 .
Please help.
Done. I change the following:
In Association class:
#AssociationOverrides({
#AssociationOverride(name = "idPk.appPk", joinColumns = { #JoinColumn(name = "ID_APP", referencedColumnName = "ID_APP"),
#JoinColumn(name = "VERSION", referencedColumnName = "VERSION") }),
#AssociationOverride(name = "idPK.customerPk", joinColumns = #JoinColumn(name = "ORG_ID"))
})
I'm creating a MySQL database as followed :
database design
the Country and Province tables are pre-filled with data. I have the application running and can get stuff no problem, and also the join table person_has_address works when getting.
however, when I insert data using post I want to be able to set the ID of the province, and let spring data jpa just add that number to add_pro_id in the Address table. For example, when I post the following json:
{ "firstName":"bilbo", "lastName":"baggings", "address":{"street":"streetName", "streetNum":3, "zipcode":"1337GG", "city":"TheCity", "province":{"name":"aProvinceName"}} }
jpa should see that aProvinceName exists and grab that id and add that to add_pro_id.
Now it just insert aProvinceName as new value in province and add the new id to add_pro_id.
The person class:
#Entity
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="per_id")
private int id;
#Column(name="per_name")
private String firstName;
#Column(name="per_surname")
private String lastName;
#Column(name="per_birth_date")
private String birthDate;
#Column(name="per_fax")
private String fax;
#Column(name="per_phone")
private String phone;
#Column(name="per_email")
private String email;
#OneToOne(optional = false, cascade = CascadeType.ALL)
#JoinTable(name="person_has_address", joinColumns = {#JoinColumn(name="pha_per_id", referencedColumnName = "per_id")}, inverseJoinColumns = {#JoinColumn(name="pha_add_id", referencedColumnName = "add_id")})
private Address address;
// getters and setters
This is the person repository:
#RepositoryRestResource(collectionResourceRel = "people", path = "people")
public interface PersonRepository extends PagingAndSortingRepository<Person, Long> {
List<Person> findByLastName(#Param("name") String name);
}
This is the address class:
#Entity
public class Address {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="add_id")
private int id;
#Column(name = "add_street")
private String street;
#Column(name="add_street_num")
private int streetNum;
#Column(name="add_zip")
private String zipcode;
#Column(name="add_city")
private String city;
#JoinColumn(name="add_pro_id", referencedColumnName = "pro_id")
#ManyToOne(optional=false, cascade = CascadeType.ALL)
private Province province;
// getters and setters
Province class:
#Entity
public class Province {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="pro_id")
private int id;
#Column(name="pro_name")
private String name;
#ManyToOne
#JoinColumn(name="pro_cou_id")
private Country country;
// getters and setters
And lastly country class:
#Entity
public class Country {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="cou_id", insertable = false, updatable = false)
private int id;
#Column(name="cou_name", insertable = false, updatable = false)
private String name;
// getters and setters
I've tried adding insertable = false and updatable = false, but the application then just inserts NULL values in my database. I've also tried working with #primarykeyjoins, but to no success.
if anyone knows how I should tackle this problem I would much appreciate it!
Thanks in advance.
I have two Entity classes "Teacher & Class" where there is a #OneToMany relationship between them. The first one has a rest interface at /teachers/{id} and the second one has a rest interface at /classes/{id}. When a user sends a GET request to the Teacher interface, he should receive all of the Teacher and Class fields. But, when the user sends a GET request to the Class interface, I would like him to receive all of the Class fields and only a part of the Teacher fields "firstName, lastName"
Here are the entities code:
#Entity
#DiscriminatorValue("Th")
public class Teacher{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String firstName;
private String lastName;
private String phone;
#Column(unique = true)
#Email
private String email;
private String password;
#OneToMany(mappedBy = "teacher", fetch = FetchType.EAGER)
private List<Class> classes;
#OneToMany(mappedBy = "teacher", fetch = FetchType.EAGER)
private List<Note> notes;
protected Teacher() {
}
}
#Entity
public class Class {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String name;
#OneToOne()
#JoinColumn(name = "subject_id")
private Subject subject;
#Enumerated(EnumType.STRING)
private Grade grade;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "teacher_id")
private Teacher teacher;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "class_student", joinColumns = { #JoinColumn(name = "class_id", referencedColumnName = "id") }, inverseJoinColumns = { #JoinColumn(name = "student_id", referencedColumnName = "id") })
private List<Student> students;
protected Class() {
}
}
//getters and setters
Your request sounds mutualy exclusive, don't load Teacher object but do load it for it's ID.
You are better off then making the teacher object lazy load and then initialise it once class is loaded.
FYI
#JsonIgnore prevents loading of objects when RESTfull calls are made.