I'm building app to learn Hibernate on PostgreSQL. And I'm currently trying to add variable to database that has OneToOne relationship.
First, I create two tables in my database with schema below. On person_detail table it has primary key that's also foreign key to person table.
Then I created two classes, Person and PersonDetail. PersonDetail is child of Person that has OneToOne relationship. And I use code below to add person with personDetail as attribute.
Person person = new Person(
"Rick",
1.7,
dateFromString("1969-4-2"),
new Date()
);
PersonDetail personDetail =
new PersonDetail("myemail#email.com", "Marley");
person.setPersonDetail(personDetail);
session.beginTransaction();
session.save(person);
session.save(personDetail);
session.getTransaction().commit();
System.out.println(person.toString());
But the problem with code above is that Hibernate execute child query first instead of parent query.
Hibernate: insert into person_detail (address, email) values (?, ?)
And since person still empty, we cannot insert any row to person_detail because it violates foreign key constraint.
Is there a way to solve this?
Thanks!
In case some one want to check how I annotate those two classes, I put the code below.
#Entity
#Table(name="person")
#Data
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="id")
private int id;
#Column(name="name")
private String name;
#Column(name="height")
private double height;
#Column(name="birth_date")
private Date dateBirth;
#Column(name="last_seen")
private Date lastSeen;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "id")
private PersonDetail personDetail;
public Person() {}
public Person(String name, double height, Date dateBirth, Date lastSeen){
this.name = name;
this.height = height;
this.dateBirth = dateBirth;
this.lastSeen = lastSeen;
}
}
#Data
#Entity
#Table(name="person_detail")
public class PersonDetail {
#Id
#Column(name="id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(name="email")
private String email;
#Column(name="address")
private String address;
public PersonDetail(){}
public PersonDetail(String email, String address){
this.email = email;
this.address = address;
}
}
I see that you have primary key in table person_details as foreign key to person table, you can use #PrimaryKeyJoinColumn like this:
#Entity
#Table(name="person")
#Data
public class Person {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "name")
private String name;
#Column(name = "height")
private String height;
#OneToOne(mappedBy = "person", cascade = CascadeType.ALL)
#PrimaryKeyJoinColumn
private PersonDetail personDetail;
public Person() {}
public Person(String name, String height) {
this.name = name;
this.height = height;
}
}
#Data
#Entity
#Table(name="person_detail")
public class PersonDetail {
#Id
#Column(name="id")
private Long id;
#Column(name="email")
private String email;
#OneToOne
#MapsId
#JoinColumn(name = "id", referencedColumnName = "id")
private Person person;
public PersonDetail(){}
public PersonDetail(String email){
this.email = email;
}
}
And if you save your entity, don't forget set Person to PersonDetails:
Person person = new Person("Rick", "1.7");
PersonDetail personDetail = new PersonDetail("myemail#email.com");
personDetail.setPerson(person);
person.setPersonDetail(personDetail);
repository.save(person);
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)
I have a to make a one-to-one association between two Entities, but one of them must have two #Id. One is PRI another one is MUL. How must i declare composite id, and how do i need to map the classes?
#Entity
#Table(name = "PERSONS")
public class Person implements Serializable{
private static final long serialVersionUID = -3451407520028311143L;
#Id
#Column(name = "ID")
private Integer id;
#Column(name = "ADDRESS_ID")
private Integer addressId;
#Column(name ="NAME")
private String name;
#OneToOne(mappedBy= "person", cascade= CascadeType.ALL)
private Address address;
...
}
second class is mapped via #IdClass annotation
#Entity
#Table ( name = "ADDRESS" )
#IdClass(AddressKeys.class)
public class Address implements Serializable {
#Id
#Column ( name = "ID")
private Integer id;
#Id
#Column ( name = "PERSON_ID")
private Integer idPerson;
#Column ( name = "CITY" )
private String city;
#OneToOne(cascade= CascadeType.ALL)
#JoinColumn(name="PERSON_ID")
private Person person;
...
}
and the id class
class AddressKeys implements Serializable{
private Integer id;
private Integer idPerson;
//getters and setters
#Override
public int hashCode() {
...
return result;
}
#Override
public boolean equals(Object obj) {
...
}
}
So when i try to create and save a record i have a next error
Could not open sessionRepeated column in mapping for entity:
hibernateMappedModels.base1.mappedClasses.oneToOne.Address column:
PERSON_ID (should be mapped with insert="false" update="false")
java.lang.NullPointerException at
hibernateMappedModels.base1.Main.run(Main.java:45) at
hibernateMappedModels.base1.Main.main(Main.java:24
I tryed to make an Id fields unInsertable and unUpdatable, and it was working, but i need them to be insertable and updatable; Is there any possibility to do it?
I am confused by your mappings and not sure what is required other then the simple mappings below: if I am missing something then you will need to expand on your question. You are getting the error as you have mapped the column twice - once via the one-to-one and once as a simple property. Additionally, I am not sure why you require a composite key on address.
#Entity
#Table(name = "PERSONS")
public class Person implements Serializable{
private static final long serialVersionUID = -3451407520028311143L;
#Id
#Column(name = "ID")
private Integer id;
#Column(name ="NAME")
private String name;
#OneToOne(mappedBy= "person", cascade= CascadeType.ALL)
private Address address;
}
#Entity
#Table ( name = "ADDRESS" )
public class Address implements Serializable {
#Id
#Column ( name = "ID")
private Integer id;
#Column ( name = "CITY" )
private String city;
#OneToOne(cascade= CascadeType.ALL)
#JoinColumn(name="PERSON_ID")
private Person person;
}
I have two tables:
Person
ID | NAME | EMAIL_ID (foreign key of Email.ID)
Email
ID | EMAIL_ADDRESS
I need to pull back the data into the following Entity but I am unsure how to join PERSON.EMAIL_ID with EMAIL.ID using annotations
#Entity
#Table(name = "PERSON")
public class PersonEntity {
#Column(name = "ID")
private String id;
#Column(name = "NAME")
private String name;
// How do I do a one to one join here?
private String emailAddress;
}
How can I use annotations properly so that the emailAddress field maps to the EMAIL.EMAIL_ADDRESS column?
Your Person entity should join with the Email entity and not the emailAddress property directly.
#Entity
#Table(name = "PERSON")
public class PersonEntity {
#Column(name = "ID")
private String id;
#Column(name = "NAME")
private String name;
#OneToOne(fetch = FetchType.LAZY, mappedBy = "person", cascade = CascadeType.ALL)
private Email email;
}
But it is strange to have an entity only for emails. Do you only ensure email is unique ? In this case Person entity can have an emailAddress with #Column(unique = true).
#Entity
#Table(name = "PERSON")
public class PersonEntity {
#Column(name = "ID")
private String id;
#Column(name = "NAME")
private String name;
#Column(unique = true)
private String emailAddress;
}
You usually don't do joins in Hibernate. Rather use mapping and HQL.
A good example can be found here :
http://viralpatel.net/blogs/hibernate-one-to-one-mapping-tutorial-using-annotation/
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 would like to make a JPA query to count how many records are with a specific foreign key.
Entities:
#Entity(name = "person")
#Inheritance(strategy = InheritanceType.JOINED)
public class Person {
#Id
private Long id;
private String firstName;
private String lastName;
private String phoneNumber;
#OneToMany(fetch = FetchType.LAZY, cascade = {CascadeType.ALL})
#JoinColumn(name = "ADRESS_FK")
private ADDRESS address;
}
#Entity(name = "address")
public class Address {
#Id
private Long id;
private String country;
private String city;
private String street;
private String number;
}
And this is what i am trying, how would this look in JPA?
public Long getOccurences( Long id ) {
Query query = _entityManager.createQuery( "Select count(*) from Persons p where p.address = ?1" );
query.setParameter( 1, id );
return query.getSingleResult() );
}