My Users are in Organisations in a ManyToOne relationship, when a user is created with an existing Organisation I am trying to assign it to it without creating a new one.
In my service, here is how I create a user:
#Override
public UserInfo createUser(UserInfo newUser) {
// Check if organisation exists
OrganisationEntity orga = organisationDao.findByName(newUser.getOrganisation());
if (orga != null) {
// Organisation exists, we save it with the correct ID
return mapper.map(userDao.save(mapper.map(newUser, orga.getId())));
} else {
// Organisation does NOT exists, we save it and create a new one
return mapper.map(userDao.save(mapper.map(newUser, (long) -1)));
}
}
With my Mapper (helping me to convert a model to an entity) being:
public UserEntity map(UserInfo userInfo, Long orgaId) {
UserEntity user = new UserEntity();
user.setEmail(userInfo.getEmail());
user.setFirstName(userInfo.getFirstName());
user.setLastName(userInfo.getLastName());
user.setPassword(userInfo.getPassword());
OrganisationEntity orga = new OrganisationEntity();
orga.setName(userInfo.getOrganisation());
// We set the organisation's ID
if (orgaId != -1)
orga.setId(orgaId);
user.setOrganisation(orga);
return user;
}
And here is my UserDao:
#Transactional
public interface UserDao extends CrudRepository<UserEntity, Long> {
UserEntity save(UserEntity user);
}
And finally the relation in my UserEntity:
#ManyToOne(targetEntity = OrganisationEntity.class, cascade = CascadeType.ALL)
#JoinColumn(name = "orga_id")
private OrganisationEntity organisation;
Creating a user with a new Organisation work but when I input an existing one, I get the following:
detached entity passed to persist
From my understanding it is a bidirectional consistency problem, but the answers did not help me so far.
Finally here are my Entity classes:
#Entity
#Table(name = "\"user\"")
public class UserEntity {
#Id
#Column(name = "user_id")
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#NotNull
private String email;
#NotNull
private String firstName;
#NotNull
private String lastName;
#NotNull
private String password;
#ManyToOne(targetEntity = OrganisationEntity.class, cascade = CascadeType.ALL)
#JoinColumn(name = "orga_id")
private OrganisationEntity organisation;
// Getters & Setters
}
and
#Entity
#Table(name = "organisation")
public class OrganisationEntity {
#Id
#Column(name = "orga_id", unique = true)
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#NotNull
#Column(unique = true)
private String name;
// Getters & Setters
}
I have solved my problem,
As you can see in the mapper above, I am creating a new instance of OrganisatonEntity no matter what, even if it already exists !
So a small change in my code solved it:
public UserEntity map(UserInfo userInfo, OrganisationEntity organisationEntity);
instead of
public UserEntity map(UserInfo userInfo, Long orgaId);
When the organisation already exists, I then assign it to my UserEntity like such:
user.setOrganisation(organisationEntity);
instead of instantiating a new object.
Problem solved !
Related
this is my code:
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
private String password;
#OneToOne(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.EAGER, optional = false)
private Employee employee;
public User() {
}
//SETTERS AND GETER
}
#Entity
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
#OneToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "user_id")
private User user;
public Employee() {
}
//SETTERS AND GETER
}
On my service I'm trying somthing like this:
User user = new User();
user.setUsername("my_username");
user.setPassword("12345");
Employee employee = new Employee();
employee.setName("My Name");
employee.setBirth(LocalDate.now());
user.setEmployee(employee);
userService.save(user);
There Are no errors nor any problem on the application, but looking on my database, the user_id column is empty, what is for me to do to have user_id set automatically with User id? Thanks in advance!
As it is stated in the hibernate documentation:
Whenever a bidirectional association is formed, the application developer must make sure both sides are in-sync at all times.
You use bidirectional #OneToOne so, you should synchronize both side of the association:
User user = new User();
user.setUsername("my_username");
user.setPassword("12345");
Employee employee = new Employee();
employee.setName("My Name");
employee.setBirth(LocalDate.now());
// make both side of bidirectional #OneToOne in-sync
user.setEmployee(employee);
employee.setUser(user);
userService.save(user);
When I'm trying to save an U object I got next exception:
org.springframework.orm.jpa.JpaSystemException: attempted to assign id from null one-to-one property [com.roc.domain.A.user]; nested exception is org.hibernate.id.IdentifierGenerationException: attempted to assign id from null one-to-one property [com.roc.domain.A.user]
I have two tables:
1. user that columns are id(auto incr, primary), name.
2. contact that columns are id, user_id(that is foreign key -> user.id) and address.
#Entity
#Table(name = "a")
public class A {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name="address")
private String address;
#OneToOne
#MapsId
private U user;
public A() {
}
// getters and setters
}
#Entity
#Table(name = "u")
public class U {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name="username")
private String userName;
#JoinColumn(name = "user_id", referencedColumnName = "id")
#OneToOne(mappedBy = "user", cascade = CascadeType.ALL)
private A a;
public U(){};
}
#RunWith(SpringRunner.class)
#SpringBootTest
public class ApplicationTest {
#Autowired
private URepository uRepository;
#Test
public void simpleCrudTest() {
U user = new U("name", new A("address"));
uRepository.save(user);
}
}
You have set the cascade correctly however because the relationship is bi-directional you need to set both sides in the in-memory model.
#Test
public void simpleCrudTest() {
U user = new U("name", new A("address"));
//will work when this is added
a.setUser(user);
uRepository.save(user);
}
Otherwise, as the error states, A has a null reference for user on save.
Edit: To save using a single repository save call.
#Entity
#Table(name = "a")
public class A {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "address")
private String address;
#OneToOne
#MapsId
private U user;
public A() {
}
}
#Entity
#Table(name = "u")
public class U {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "username")
private String userName;
#JoinColumn(name = "user_id", referencedColumnName = "id")
#OneToOne(mappedBy = "user", cascade = CascadeType.ALL)
private A a;
public U() {
};
// method to manage the bidirectional association
public U addToA(A a) {
this.a.add(a);
a.setUser(this);
}
}
#RunWith(SpringRunner.class)
#SpringBootTest
public class ApplicationTest {
#Autowired
private URepository uRepository;
#Test
public void simpleCrudTest() {
U user = new U();
user.addToA(new A("address"));
user.setUserName("username");
uRepository.save(user);
}
}
Also, you refer to this link.
inserting values into multiple tables using hibernate
You have to save A first, Then set saved A to U and save U.
#RunWith(SpringRunner.class)
#SpringBootTest
public class ApplicationTest {
#Autowired
private URepository uRepository;
#Autowired
private ARepository aRepository;
#Test
#Trascational
public void simpleCrudTest() {
A a = new A();
a.setAddress("address");
a = aRepository.save(a);
U user = new U("name", a);
uRepository.save(user);
}
}
I have two entities Employee and Review. I am trying to create a OneToOne relationship Employee <-> Review.
When I update an Employee with a review, the Employee gets updated where the review becomes the corresponding review,
but the Review doesn't get the 'reviewee' column added with the ID of the employee which is what I expect.
What am I doing wrong?
These are my entities:
public class Employee {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Integer id;
private String name;
private String email;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "reviewee")
private Review review;
}
public class Review {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Integer id;
private String body;
private char completed;
#OneToOne(mappedBy = "review")
private Employee reviewee;
}
This is my employeeController update function:
#GetMapping(path="/update")
public #ResponseBody Employee updateEmployee (#RequestParam Integer id,
#RequestParam(value = "name", required=false) String name,
#RequestParam(value = "email", required=false) String email,
#RequestParam() Integer reviewId) {
Employee n = EmployeeRepository.findOne(id);
if(name == null) {
name = n.getName();
}
if(email == null) {
email = n.getEmail();
}
n.setName(name);
n.setEmail(email);
Review r = ReviewRepository.findOne(reviewId);
n.setReview(r);
EmployeeRepository.save(n);
return n;
}
The request:
curl 'localhost:8080/employees/update?id=2&reviewId=1'
Because the owner of the relationship (the one with #JoinColumn) is Employee, you have to create/update/delete the association by saving the Employee object.
This is what you are doing so far. But Hibernate will only update the owner when you save it. You should in addition do this before returning your entity:
r.setReviewee(n);
Notice that the next time you will retrieve the review, it will correctly have an Employee object.
Beware: I smell a Jackson infinite loop there when serializing.
Employee.review -> Review -> Review.reviewee -> Employee -> Employee.review...
EDIT
To prevent the Jackson infinite loop:
1. Ignore the serialization.
Employee.java
public class Employee {
// ...
// Do not serialize this field
#JsonIgnore
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "reviewee")
private Review review;
// ...
}
2. Serialize as ID.
Employee.java
public class Employee {
// ...
// Serialize as a single value with the field "id"
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
// Serialize as told by #JsonIdentityInfo immediately (if false -> on second and further occurrences)
#JsonIdentityReference(alwaysAsId = true)
// Rename to "review_id" (would be "review" otherwise)
#JsonProperty(value = "review_id")
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "reviewee")
private Review review;
// ...
}
3. Alternative to serialize as ID: read-only reference to the foreign key.
Employee.java
public class Employee {
// ...
// Do not serialize this field
#JsonIgnore
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "reviewee")
private Review review;
// Read-only access to the foreign key
#Column(name = "Review_id", insertable = false, updatable = false)
private Integer reviewId;
// ...
}
It's seems to be a configuration mismatch. Please try the below one.
public class Employee {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Integer id;
private String name;
private String email;
#OneToOne(mappedBy="reviewee",cascade = CascadeType.ALL)
private Review review; }
public class Review {
#Id
#GeneratedValue(generator="gen")
#GenericGenerator(name="gen", strategy="foreign", parameters={#Parameter(name="property",value="reviewee")})
private Integer id;
private String body;
private char completed;
#OneToOne
#PrimaryKeJoinCloumn
private Employee reviewee; }
I hope the above configuration works as you expected.
Please make sure you're calling the save function under Transaction boundary. Otherwise don't forget to call flush() before closing the session.
I have two tables I need to insert in to in Hibernate - I have a User and every user belongs is a Member. Therfore when creating a new user I need a new entry in the Member table. I have attempted this via creating a Member object which maps to my member table and then having that as a field in my User object which maps to the user table
#Entity
#Table(name = "USER")
public class User
{
#Id
#GeneratedValue
#Column(name = "id")
private int id;
#Column(name = "username")
private String username;
#Column(name = "password")
private String password;
#Column(name = "fullName")
private String fullName;
//other fields ommited
#OneToOne
#JoinColumn(name = "id")
private Member member;
My member pojo looks as follows
#Entity
#Table(name = "MEMBER")
public class Member
{
#Id
#GeneratedValue
#Column(name = "id")
private int id;
#Column(name = "sgpid")
private int sgpid;
#Column(name = "username")
private String username;
Trying to save the object i do as follows;
#Override
public boolean addUser(User user)
{
if (user == null)
{
throw new IllegalArgumentException("Unable to add null user");
}
Session session = HibernateUtil.getSessionFactory().openSession();
session.beginTransaction();
session.save(user);
session.getTransaction().commit();
return true;
}
This gives me the row saved in my user table but the entry is not inserted in to the member table. I think my linking annotations are probably incorrect but I am not too sure - please could someone provide some assistance.
Thanks
Try to set the cascade value of the #OneToOne annotation:
#OneToOne(cascade = CascadeType.PERSIST)
#JoinColumn(name = "id")
private Member member;
First thing in your user class you should change the joinColumn to member_id.
As mentioned in another answer to persist a related entity you need to set the cascade to persist, i would recommend using cascade All which will involve the related entity in all operations check the doc
https://docs.oracle.com/cd/E19798-01/821-1841/bnbqm/index.html
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "member_id")
private Member member;
I'm using Spring with Hibernate as a JPA provider and are trying to get a #OneToMany (a contact having many phonenumbers) to save the foreign key in the phone numbers table. From my form i get a Contact object that have a list of Phone(numbers) in it. The Contact get persisted properly (Hibernate fetches an PK from the specified sequence). The list of Phone(numbers) also gets persisted with a correct PK, but there's no FK to the Contacts table.
public class Contact implements Serializable {
#OneToMany(mappedBy = "contactId", cascade = CascadeType.ALL, fetch=FetchType.EAGER)
private List<Phone> phoneList;
}
public class Phone implements Serializable {
#JoinColumn(name = "contact_id", referencedColumnName = "contact_id")
#ManyToOne
private Contact contactId;
}
#Repository("contactDao")
#Transactional(readOnly = true)
public class ContactDaoImpl implements ContactDao {
#Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
public void save(Contact c) {
em.persist(c);
em.flush();
}
}
#Controller
public class ContactController {
#RequestMapping(value = "/contact/new", method = RequestMethod.POST)
public ModelAndView newContact(Contact c) {
ModelAndView mv = new ModelAndView("contactForm");
contactDao.save(c);
mv.addObject("contact", c);
return mv;
}
}
Hopefully I got all of the relevant bits above, otherwise please let me know.
You have to manage the Java relationships yourself. For this kind of thing you need something like:
#Entity
public class Contact {
#Id
private Long id;
#OneToMany(cascade = CascadeType.PERSIST, mappedBy = "contact")
private List<Phone> phoneNumbers;
public void addPhone(PhoneNumber phone) {
if (phone != null) {
if (phoneNumbers == null) {
phoneNumbers = new ArrayList<Phone>();
}
phoneNumbers.add(phone);
phone.setContact(this);
}
}
...
}
#Entity
public class Phone {
#Id
private Long id;
#ManyToOne
private Contact contact;
...
}
In reply to Cletus' answer. I would say that it's important to have the #column annotation on the id fields, as well as all the sequence stuff. An alternative to using the mappedBy parameter of the #OneToMany annotation is to use the #JoinColumn annotation.
As a kinda aside your implementation of addPhone needs looking at. It should probably be something like.
public void addPhone(PhoneNumber phone) {
if (phone == null) {
return;
} else {
if (phoneNumbers == null) {
phoneNumbers = new ArrayList<Phone>();
}
phoneNumbers.add(phone);
phone.setContact(this);
}
}
If the Contact-Phone relationship is unidirectional, you can also replace mappedBy in #OneToMany annotation with #JoinColumn(name = "contact_id").
#Entity
public class Contact {
#Id
private Long id;
#OneToMany(cascade = CascadeType.PERSIST)
#JoinColumn(name = "contact_id")
private List<Phone> phoneNumbers;
// normal getter/setter
...
}
#Entity
public class PhoneNumber {
#Id
private Long id;
...
}
Similar in JPA #OneToMany -> Parent - Child Reference (Foreign Key)
I don't think the addPhone method is necessary, you only have to set the contact in the phone object:
phone.setContact(contact);
If you want your relationship unidirectional i.e. can navigate from Contact to Phone's only, you need to add
#JoinColumn(name = "contact_id", nullable = false)
Under your #OneToMany on your parent entity.
nullable = false IS VITAL if you want hibernate to populate the fk on the child table
Try this sample:
#Entity
public class Contact {
#Id
private Long id;
#JoinColumn(name = "contactId")
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
private Set<Phone> phones;
}
#Entity
public class Phone {
#Id
private Long id;
private Long contactId;
}
In JPA this helped me
contact.getPhoneList().forEach(pl -> pl.setContact(contact));
contactRepository.save(contact);