Refresh one-to-many relationship after child was added - java

So I have two entities:
Person.java
#Entity
#Table(name = "persons")
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#OneToMany(mappedBy="person", cascade = CascadeType.REMOVE)
#JsonIgnoreProperties("person")
private Set<Address> addresses;
//getters and setters
}
Address.java
#Entity
#Table(name = "addresses")
public class Address {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#NotNull
#ManyToOne
#JoinColumn(name = "person_id", nullable = false)
#JsonIgnoreProperties("addresses")
private Person person;
//getters and setters
}
And later on in my code I have a personDb object (already saved in database) and then I add Address:
Address address = new Address();
address.setPerson(personDb);
address = addressRepository.save(address);
and now I have address object with person object attached to it but my personDb still doesn't have any addresses attached to it. Even if I try to get it from database once more:
personRepository.findOne(personDb.getId());
I have null where should be set of addresses. I also tried changing my annotation in Person class to something like this:
#OneToMany(mappedBy="person", fetch = FetchType.EAGER, cascade = CascadeType.REMOVE)
or changing CascadeType to ALL but nothing helps. What can I do to load addresses to my personDb object after they were added to database?

This is not best solution, but try to add also new address to personDB.
Address address = new Address();
address.setPerson(personDb);
personDB.addAddress(address);
personRepo.save(personDB)

Make sure the person is persisted.
For that make an integration test for it. If you are using Spring, I also suggest you use in-memory db for your tests.
#Transactional
#Test
public void testFindOneAddress(){
// persist the person
Person person = new Person();
...
personRepository.save(person);
// persist the address
Address address = new Address();
address.setPerson(person);
addressRepository.save(address);
// find the persisted person and addresses
Person queryResult= personRepository.findOne(person.getId());
assertNotNull(queryResult);
assertNotNull(queryResult.getAddresses());
assertEquals(1, queryResult.getAddresses().size());
assertEquals(address.getId(), queryResult.getAddresses().get(0).getId());
}
Also make sure you person id column is called "person_id"
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "person_id")
private Integer id;
take it from there

Related

JPA Hibernate : A different object with the same identifier value was already associated with the session

I am using two address for a person i.e. present and permanent address.
Each address has an District.
I am showing a district list in a dropdown at the frontend so that user can select a district among them.
So this case must be occur that one has present and permanent address same. So one can select two same district for both address.
Now come to the point. If one set same district for both address, JPA Hibernate show an error following as at the same time two same district are being updated.
nested exception is javax.persistence.EntityExistsException: A different object with the same identifier value was already associated with the session
Person.java
#Table(name = "person")
public class Person implements Serializable {
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER, optional = true)
#JoinColumn(name = "permanent_adrs_id", referencedColumnName = "id")
private Address permanentAddress;
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER, optional = true)
#JoinColumn(name = "present_adrs_id", referencedColumnName = "id")
private Address presentAddress;
}
Address.java
#Table(name = "address")
public class Address implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String village;
#ManyToOne(fetch = FetchType.EAGER, optional = true)
#JoinColumn(name = "district_id", referencedColumnName = "id")
private District district;
}
District.java
#Table(name = "district")
public class District implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
}
My guess is, you have two District or Address objects that have the same value for id, but are different objects i.e. obj1 != obj2.
This is illegal in JPA. The usual solution to this is, to load objects which you refer to from other objects.
For example, do something like this:
Person p = ...
p.getPermanentAddress().setDistrict(
entityManager.find(District.class, p.getPermanentAddress().getDistrict().getId())
);
p.getPresentAddress().setDistrict(
entityManager.find(District.class, p.getPresentAddress().getDistrict().getId())
);
repository.save(p);

How to manage OnetoOne inserting data in child only

I am very new to hibernate and I am working with JPA and Hibernate4. Trying to insert parent object in child as onetoone relationship.
I went through some tutorials but All the example in the web shows, inserting both parent and child tables.
I want to insert data in child table only.
I have two tables called user and department.
User table consists of user details with department as onetoone relationship, as follows,
#Entity
#Table(name = "User")
public class UserEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "_id")
private String id;
#Column(name = "firstName")
private String firstName;
#Column(name = "lastName")
private String lastName;
#OneToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "departmentId")
private Department departmentId;
// getters and setters...
}
Below is my Department entity,
#Entity
#Table(name = "Department")
public class Department {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "_id")
private String id;
#Column(name = "name")
private String name;
// getters and setters...
}
In department table there is only 4 data. I want to insert data only in user data while insert into it and don't want to insert in Department.
How can I do that.Please assist.
You have to use mappedBy for this, as mentoned below in child Table, Department in your case
#OneToOne(mappedBy="department")
private UserEntity user;
These posts explain you better this,
JPA JoinColumn vs mappedBy
Understanding mappedBy annotation in Hibernate
You need to specify the relationship owner using mappedBy property in the OneToOne mapping in the owner side, here in your case in the Department class, you should add:
#OneToOne(mappedBy="department")
private UserEntity user;
I updated your code, to included the stated annotation and also renamed the Department property in your UserEntity class from departmentId to department to avoid confusion between relationship owner and its id:
#Entity
#Table(name = "User")
public class UserEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "_id")
private String id;
#Column(name = "firstName")
private String firstName;
#Column(name = "lastName")
private String lastName;
#OneToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "departmentId")
private Department department;
// getters and setters...
}
Below is the Department entity,
#Entity
#Table(name = "Department")
public class Department {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "_id")
private String id;
#Column(name = "name")
private String name;
#OneToOne(mappedBy="department")
private UserEntity user;
// getters and setters...
}
This will give you the right mapping with the expected behaviour.
In the #OneToOne annotation, the default value for parameter optional is true. So your annotation is the same as #OneToOne(fetch = FetchType.EAGER, optional = true). This means you can simply leave the Department in a UserEntity instance empty. In that case, persisting it results in persisting only a user entity and no department.
Even if you created a Department instance and assigned it to a UserEntity instance, persisting the UserEntity would not automatically persist the Department, since you don't have any cascade parameter in your annotation. If you don't automatically cascade persists, you would have to persist the Department first and then persist the corresponding user entity.
Maybe you're asking about using existing departments for your user entities. In that case, you first need to get the department via Hibernate (or the JPA API) from an entity manager. The entity instance you get is managed by Hibernate, and you can then set it in a UserEntity and persist that, to have it refer to the department.
Finally, I think one department will probably have more than one user. It might make more sense to have a #ManyToOne annotation instead of #OneToOne, indicating multiple users can refer to the same department, but that depends on your domain model.

Hibernate one to many automatically insert id

So i have a very basic construction where i have a client and this client could have multiple addresses.
So in hibernate i did something like this
#Entity
#Table(name ="tbl_clients")
#Access(value = AccessType.FIELD)
public class Client {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id_client")
private Integer id;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "fkIdClientAddress", cascade = CascadeType.ALL)
private List<AddressClient> addressClientList = new ArrayList<>();
And the other class looks like this :
#Entity
#Table(name ="tbl_clients_address")
#Access(value = AccessType.FIELD)
public class AddressClient {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id_client_address")
private Integer id;
#ManyToOne(cascade=CascadeType.ALL)
#JoinColumn(name="id_client")
private Client Client;
#Column
private Integer fkIdClientAddress;
When inserting a client into the database who has 2 addresses it works but the fields fkIdClientAddress and id_client is are empty in the database. So i have no idea to who the address belong.
How can i fix this? And what is wrong with this construction?
first improvements
Class AddressClient
#Entity
#Table(name ="tbl_clients_address")
#Access(value = AccessType.FIELD)
public class AddressClient {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id_client_address")
private Integer id;
#ManyToOne
#JoinColumn(name="id_client")
private Client client;
Class Client
#Entity
#Table(name ="tbl_clients")
#Access(value = AccessType.FIELD)
public class Client {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id_client")
private Integer id;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "client", cascade = CascadeType.ALL)
private List<AddressClient> addressClientList = new ArrayList<>();
This is looking better but the field id_client is still null
When i created a for each and save the AddressClients again the id is successful saved.
#RequestMapping(value = "/addclient",method = RequestMethod.POST)
public void addClient(#AuthenticationPrincipal Principal user,#RequestBody Client client) {
//FIND THE ACTIVE USER
LOGGER.info("SQL: GET PRINCEPAL USER");
User getuser = userDao.findByEmail(user.getName());
for (AddressClient addressClient : client.getAddressClientList())
{
addressClient.setClient(client);
}
clientDao.save(client);
}
Your mapping is wrong. First of all, you don't need two different columns (id_client and fkIdClientAddress) to know that a given address belongs to a given client. So the first thing to do is to remove fkIdClientAddress.
Then the mappedBy attribute in OneToMany is usd to tell Hibernate which field in address represents the owning ManyToOne association. So it must be set to Client. (or, if you respect the Java naming conventions and rename the field to client, it must be set to client).
Finally, having cascade=ALL on a ManyToOne doesn't make much sense: you don't want to delete the client when you delete one of its addresses. That would fail anyway, since other addresses would still reference the client.
Your mappings are wrong. When you define the collection in Client class, you should indicate the field in the AddressClient class which points back to the Client and that is the field client.
#OneToMany(fetch = FetchType.LAZY, mappedBy = "client", cascade = CascadeType.ALL)
private List<AddressClient> addressClientList = new ArrayList<>();
And in ClientAddress class you have:
#ManyToOne(cascade=CascadeType.ALL)
#JoinColumn(name="id_client")
private Client client;
You do not need the field fkIdClientAddress
In some cases, you still can keep column
#Column
private Integer fkIdClientAddress;
But you have to set data for this column instead of setting value for
private Client client;
But this approach is not appropriate.

JPA 2: Order by not working in ManyToMany with extra fields

I have a ManyToMany which I have mapped like this question.
Please note, I have removed boilerplate for simplicity
#Entity
class Person {
#OneToMany(mappedBy = "person")
#OrderBy("sort")
private List<PersonAddress> adresses = new ArrayList<>();
}
#Entity
class PersonAdress {
#EmbeddedId
private PersonAdressId id;
#Column
private int sort;
#ManyToOne
private Person person;
#ManyToOne
private Address address;
}
#Entity
class Address {
#OneToMany(mappedBy = "address")
#OrderBy("sort")
private List<PersonAddress> persons = new ArrayList<>();
}
#Embeddable
public class PersonAdressId implements Serializable {
#Column(name = "person_id")
private long personId;
#Column(name = "address_id")
private long addressId;
}
I am trying to get all the adresses for person, and order by the sort attribute.
But for some reason I get exception or I don't get it sorted.
I have tried the following:
"select p from Person p where p.id=pid join fetch p.address a order by a.sort"
I have also tried:
Person person = entityManager.find(Person.class, personId);
person.getAddress() //<-- This should use the #OrderBy, but I don't get it ordered nor does it print out order by in the output
Can anyone spot why its not working?
Did you try following query:
Select p From Person p Left Join p.adresses a Where p.id = :pid Order By a.sort
See the HQL reference for more information.
To start with, your mapping is false : you want a many-to-many association between Person and Address ? and that's why you have that PersonAddress entity in the middle ?
In class Person, it should be :
#OneToMany(mappedBy = "person")
private List<PersonAddress> personAdresses = new ArrayList<>();
In class Address, it should be :
#OneToMany(mappedBy = "address")
private List<PersonAddress> personAddresses = new ArrayList<>();
Only then, your sort will work, as it is a member of PersonAddress entity

Hibernate, Cascaded operations, Duplicates

I have a many to many relation between 2 tables Person and Address with a middle table Person_Address. I have Daos and Services for these entities
What i want to have is when a person is added with an address list if the addresses already exists in the database system should just add id's of those addresses to the Person_Address table.
What happens is duplicate values with different ids.
Possible way to do this is to check the database before adding but if i were to do that then i would be writing my own SQL queries instead of hibernate. Is there a way for me to achieve my objective in hibernate?
Person Class
#Entity
#Table(name = "PERSON")
public class Person {
#Id
#Column(name = "personID")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
private String surName;
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinTable(name = "PERSON_ADDRESS", joinColumns = {
#JoinColumn(name = "PERSONID")}, inverseJoinColumns = {
#JoinColumn(name = "ADDRESSID")})
private List<Address> addresses = new ArrayList<>();
Address Class
#Entity
#Table(name = "ADDRESS")
public class Address {
#Id
#Column(name = "addressID")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#ManyToMany(cascade = CascadeType.ALL, mappedBy = "addresses")
private List<Person> residents;
private String street;
private String state;
private String country;
private String postCode;
If Entity have no ID. Hibernate creates it differently updates.
You should use prefilling of Address in frontend. Or use Criteria API for searching of entered of Address before saving
Maybe one solution can be to change your database and set the complete adress as the primary key, you can get it by concatenating the others fields. To be sure that the adresses will be identical i think you will have to format it to lower or to upper case.
So when you will put the adresse it will be the primary key and you will not have duplicate datas.

Categories