#Entity
public class Person implements Serializable{
#Id
#GeneratedValue
private int id;
private String name;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "person")
private List<Car> cars;
//getters and setters
}
#Entity
public class Car implements Serializable{
#Id
#GeneratedValue
private int id;
private String name;
#ManyToOne
#JoinColumn(name = "person_id")
private Person person;
// getters and setters
}
And.. I use it thus..
Person per = new Person();
per.setName("some");
Car car1 = new Car();
car1.setName("Ford");
Car car2 = new Car();
car2.setName("WagonR");
//s.save(car1);
//s.save(car2);
per.setCars(new ArrayList<Car>());
per.getCars().add(car1);
per.getCars().add(car2);
s.save(per);
Now.. the table Car has a column person_id but its showing null for both the cars.. what am I doing wrong here ? The table Person is correctly being filled. If I remove the "mappedBy" from the Person table.. and instead include the #JoinColumn here... then it works fine.
With your mapping, the owner of the relation is the Car and not the Person ... That's why when saving the Person, the Car is not saved. If you remove the mappedBy then the owner of the relation becomes the Person and you get your expected behavior!
Try calling car.setPerson(per).
Related
I am new to hibernate just stuck in map annotation in hibernate
CASE 1) #MapKey
When you use a Map you always need to associate at least two entities. Let's say we have
an Owner entity that relates to the Car entity (Car has a FK to Owner). So, the Owner will have a Map of Car(s):The #MapKey will give you the Car's property used to group a Car to its Owner. For instance, if we have a vin (Vehicle Identification Number) property in Car, we could use it as the carMap key:
#Entity
public class Owner {
#Id
private long id;
#OneToMany(mappedBy="owner")
#MapKey(name = "vin")
private Map<String, Car> carMap;
}
#Entity
public class Car {
#Id
private long id;
#ManyToOne
private Owner owner;
private String vin;
}
CASE 2) #MapKeyEnumerated
The #MapKeyEnumerated will use an Enum from Car, like WheelDrive:
#Entity
public class Owner {
#Id
private long id;
#OneToMany(mappedBy="owner")
#MapKeyEnumerated(EnumType.STRING)
private Map<WheelDrive, Car> carMap;
}
#Entity
public class Car {
#Id
private long id;
#ManyToOne
private Owner owner;
#Column(name = "wheelDrive")
#Enumerated(EnumType.STRING)
private WheelDrive wheelDrive;
}
public enum WheelDrive {
2WD,
4WD;
}
CASE 3) #MapKeyTemporal
The #MapKeyTemporal will use a Date/Calendar field for grouping, like createdOn.
#Entity
public class Owner {
#Id
private long id;
#OneToMany(mappedBy="owner")
#MapKeyTemporal(TemporalType.TIMESTAMP)
private Map<Date, Car> carMap;
}
#Entity
public class Car {
#Id
private long id;
#ManyToOne
private Owner owner;
#Temporal(TemporalType.TIMESTAMP)
#Column(name="created_on")
private Calendar createdOn;
}
Case 4) #MapKeyJoinColumn
The #MapKeyJoinColumn requires a third entity, like Manufacturer so that you have an association from Owner to Car and car has also an association to a Manufacturer, so that you can group all Owner's Cars by Manufacturer:
#Entity
public class Owner {
#Id
private long id;
#OneToMany(mappedBy="owner")
#MapKeyJoinColumn(name="manufacturer_id")
private Map<Manufacturer, Car> carMap;
}
#Entity
public class Car {
#Id
private long id;
#ManyToOne
private Owner owner;
#ManyToOne
#JoinColumn(name = "manufacturer_id")
private Manufacturer manufacturer;
}
#Entity
public class Manufacturer {
#Id
private long id;
private String name;
}
The mapping to a java.util.Map doesn't have any impact on the table mapping. In all cases, you are mapping a Many-to-One/One-to-Many associations. In your table model, this gets represented by 2 database tables. The only exception is the 4th case, where you have another entity and table but that's not part of the actual association mapping.
I explain this mapping in great details on my blog in How to map an association as a java.util.Map
To sum it up:
Case 1-3 get mapped to 2 database tables: Owner and Car.
Case 4 gets mapped to 3 database tables: Owner, Car and Manufacturer.
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
This question already has answers here:
Hibernate Criteria returns children multiple times with FetchType.EAGER
(8 answers)
Closed 6 years ago.
I have 3 Entity: PERSON, CAR, and PROFFESION.
#Entity
#Table(name = "PERSON")
public class Person implements Serializable {
#Id
private Long id;
#OneToMany(mappedBy = "person", fetch = FetchType.EAGER)
private List<Car> cars;
#OneToMany(mappedBy = "person", fetch = FetchType.EAGER)
private List<Profession> professions;
}
#Entity
#Table(name = "PROFESSION")
public class Profession implements Serializable {
#Id
private Long id;
#ManyToOne
#JoinColumn(name = "PERSON_ID")
private Person person;
}
#Entity
#Table(name = "CAR")
public class Car implements Serializable {
#Id
private Long id;
#ManyToOne
#JoinColumn(name = "PERSON_ID")
private Person person;
}
When i'm trying to retrieve a person with connections to two professions and one car the result is two professions and two duplicated cars.
Or if he is connected to five cars and one profession, the result is five cars and five duplicated professions.
How should I correct the mappings so I receive the right results?
Use a Set to map oneToMany If you don't want duplicates. Its mapped with the <set> element in mapping table. so first you make changes to these parts:
private Set<Car> car = new HashSet<Car>(0);
#OneToMany(fetch=FetchType.LAZY, mappedBy="persons")
public Set<Car> getCar() {
return this.car;
}
public void setCar(Set<Car> car) {
this.car = car;
}
Do the same with profession and another oneToMany you do not want duplicates. you can set fetchType based on loading preferences. Eager load all at once and Lazy load on demand which is usually best.
My requirement is to have a list of classes and students of each class. Each students must be in zero or at most one class. My code is as following, but in database, each student can be in many classes. How to keep each student in zero to one and only one class?
Student item table is as following
StudentItem
id student_id code
1 1 233
2 5 453
3 1 567
4 6 565
Entities
#Entity
public class MyClass{
#Id
#GeneratedValue
private long id;
#OneToMany( cascade = CascadeType.ALL)
#LazyCollection(LazyCollectionOption.FALSE)
private List<StudentItem> students;
private String season;
...
}
#Entity
public class StudentItem{
#Id
#GeneratedValue
private long id;
#OneToOne
private Student student;
private String code;
...
}
#Entity
public class Student{
#Id
#GeneratedValue
private long id;
private String fname;
private String lname;
....
}
It seems to me that it's a many-to-one relationship between MyClass and StudentItem, with MyClass owning the relationship between them. Further, it may be simpler and more straightforward to use a join table between these two entities; this way, you don't run the risk of creating more than one MyClass entry in your database.
Think of it like this: what is the actual effective relationship between a class and a student? From your description, one class may hold many students, but any given student may only be in one class.
Here's a rough-hand example from memory on how to create it. I'll assume that there is a joining table between StudentItem and MyClass called classroom_students, with columns student_item_id and class_id.
#Entity
public class MyClass {
#Id
#GeneratedValue
private Integer id;
#OneToMany
#JoinTable(name = "classroom_students",
joinColumns = #JoinColumn(name = "student_item_id"),
inverseJoinColumns = #JoinColumn(name = "class_id"))
private List<MyClass> students;
// getters and setters for entity
}
#Entity
public class StudentItem {
#Id
#GeneratedValue
private Integer id;
#OneToOne(mappedBy = "student_id")
private Student student;
#ManyToOne(targetEntity = MyClass.class,
mappedBy = "students")
private MyClass myClass;
}
I have 3 tables, each mapped to an entity. The entities are something like this:
#Entity
#Table(name = "person")
public class Person implements Serializable {
private int id;
//other fields
}
#Entity
#Table(name = "phone")
public class Phone implements Serializable {
private int id;
private Long price;
#ManyToOne
#JoinColumn(name = "personId")
private Person person;
#ManyToOne
#JoinColumn(name = "manufacturerId")
private Manufacturer manufacturer;
//other fields
}
#Entity
#Table(name = "manufacturer")
public class Manufacturer implements Serializable {
private int id;
private String name;
//other fields
}
What I want to do is to create a method that will return a list of Persons that have phones from a specified manufacturer with the price in a specified range.
EDIT: My dao class implements EntityJpaDao . I would need a solution that would work with this implementation.
Following query will return the Samsung mobile users with phone price range.
Criteria criteria = session.createCriteria(Phone.class, "phone");
criteria.createAlias("phone.person", "person")
criteria.add(Restrictions.between("phone.price", minPrice, maxPrice));
criteria.createAlias("phone.manufacturer","manufacturer");
criteria.add(Restrictions.eq("manufacturer.name", Samsung));
criteria.setProjection(Projections.property("person"));
List<Person> persons = criteria.list();