Handling Jackson Parent-Child Serialization - java

I'm using Spring MVC (am new to this), Hibernate and Jackson for my project and am using JSON for data exchange between the server and client.
I have a couple of classes "Employee" and "Address" with a ManyToOne / OneToMany relationship.
I faced the cyclic reference error, however, I was able to resolve that using #JsonManagedReference and #JsonBackReference.
But the problem is, during serialization (I'm querying the DB to get all the employees), Jackson is completely ignoring the Address property and serializing just 3 fields (other fields have been ignored specifically which you can see in the code).
Here's the JSON returned
[
{
"id": 1,
"name": "xxx",
"age": 100
},
{
"id": 2,
"name": "yyy",
"age": 100
}
]
The Employee Class:
#Entity
#Table(name = "e_employee", catalog = "emploman")
public class Employee implements java.io.Serializable {
private int id;
private String name;
private int age;
private Address address;
private String modifiedBy;
private Date modifiedTime;
private transient int addressId;
public Employee() {
}
public Employee(int id, String name, int age, Address address, String modifiedBy, Date modifiedTime) {
this.id = id;
this.name = name;
this.age = age;
this.address = address;
this.modifiedBy = modifiedBy;
this.modifiedTime = modifiedTime;
}
public Employee(String name, int age, Address address, String modifiedBy, Date modifiedTime) {
this.name = name;
this.age = age;
this.address = address;
this.modifiedBy = modifiedBy;
this.modifiedTime = modifiedTime;
}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", unique = true, nullable = false)
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
#Column(name = "name", nullable = false, length = 100)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Column(name = "age", nullable = false)
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "address", nullable = false)
#JsonBackReference("employee-address")
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
#Column(name = "modified_by", nullable = false, length = 50)
#JsonIgnore
public String getModifiedBy() {
return modifiedBy;
}
#JsonIgnore
public void setModifiedBy(String modifiedBy) {
this.modifiedBy = modifiedBy;
}
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "modified_time", nullable = false, length = 19)
#JsonIgnore
public Date getModifiedTime() {
return modifiedTime;
}
#JsonIgnore
public void setModifiedTime(Date modifiedTime) {
this.modifiedTime = modifiedTime;
}
#JsonIgnore
#Transient
public int getAddressId() {
return addressId;
}
#JsonIgnore
public void setAddressId(int addressId) {
this.addressId = addressId;
}
}
The Address Class:
#Entity
#Table(name = "e_address", catalog = "emploman")
public class Address implements java.io.Serializable {
private int id;
private String country;
private String state;
private String city;
private String streetAddress;
private String pinCode;
private String modifiedBy;
private Date modifiedTime;
private Set<Employee> employees;
public Address() {
}
public Address(int id, String country, String state, String city, String streetAddress, String pinCode, String modifiedBy, Date modifiedTime, Set<Employee> employees) {
this.id = id;
this.country = country;
this.state = state;
this.city = city;
this.streetAddress = streetAddress;
this.pinCode = pinCode;
this.modifiedBy = modifiedBy;
this.modifiedTime = modifiedTime;
this.employees = employees;
}
public Address(String country, String state, String city, String streetAddress, String pinCode, String modifiedBy, Date modifiedTime, Set<Employee> employees) {
this.country = country;
this.state = state;
this.city = city;
this.streetAddress = streetAddress;
this.pinCode = pinCode;
this.modifiedBy = modifiedBy;
this.modifiedTime = modifiedTime;
this.employees = employees;
}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
#Column(name = "country", nullable = false, length = 100)
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
#Column(name = "state", nullable = false, length = 100)
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
#Column(name = "city", nullable = false, length = 100)
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
#Column(name = "street_address", nullable = false, length = 500)
public String getStreetAddress() {
return streetAddress;
}
public void setStreetAddress(String streetAddress) {
this.streetAddress = streetAddress;
}
#Column(name = "pincode", nullable = false, length = 15)
public String getPinCode() {
return pinCode;
}
public void setPinCode(String pinCode) {
this.pinCode = pinCode;
}
#Column(name = "modified_by", nullable = false, length = 50)
public String getModifiedBy() {
return modifiedBy;
}
public void setModifiedBy(String modifiedBy) {
this.modifiedBy = modifiedBy;
}
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "modified_time", nullable = false, length = 19)
public Date getModifiedTime() {
return modifiedTime;
}
public void setModifiedTime(Date modifiedTime) {
this.modifiedTime = modifiedTime;
}
#OneToMany(mappedBy = "address", fetch = FetchType.LAZY)
#JsonManagedReference(value = "employee-address")
public Set<Employee> getEmployees() {
return employees;
}
public void setEmployees(Set<Employee> employees) {
this.employees = employees;
}
}
So, basically, the json response I'm expecting is something like below:
[
{
"id": 1,
"name": "xxx",
"age": 100,
"address": {
"country": "xxx",
"city": "abc"
}
}
]
Could anyone please help?
Update 1
I've tried getting Addresses from the DB, and employees related to that address is successfully being fetched
[
{
"id": 1,
"country": "xxx",
"state": "yyy",
"city": "zzz",
"streetAddress": "abc",
"pinCode": "12345",
"modifiedBy": "xxx",
"modifiedTime": 1400930509000,
"employees": [
{
"id": 2,
"name": "xxx",
"age": 190
},
{
"id": 1,
"name": "xxx",
"age": 200
}
]
}
]

Thanks people.
I was able to resolve this issue by using JsonIdentityInfo on my classes (both parent and child) along with a plugin called jackson-datatype-hibernate through which I enabled a feature called FORCE_LAZY_LOADING
If it helps anyone, below is the code for both the above configurations:
Create a class as below to enable jackson-datatype-hibernate
public class HibernateAwareObjectMapper extends ObjectMapper {
public HibernateAwareObjectMapper() {
Hibernate4Module hbm = new Hibernate4Module();
hbm.enable(Hibernate4Module.Feature.FORCE_LAZY_LOADING);
registerModule(hbm);
}
}
Tell Spring to use the above ObjectMapper than the default provided by Jackson:
<mvc:annotation-driven>
<mvc:message-converters>
<!-- Use the HibernateAware mapper instead of the default -->
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper">
<bean class="com.adwitiya.o2plus.utilities.HibernateAwareObjectMapper" />
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
As for the JsonIdentityInfo, below is the code. Notice the use of JsonIgnoreProperties (I've used this mainly for all the sets as it creates a recursive big tree and I wanted to avoid that)
#Entity
#Table(name = "o2_branch", catalog = "o2plus"
)
#JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class, property = "id")
#JsonIgnoreProperties(value = {"active", "modifiedBy", "modifiedTimestamp", "members", "staff"})
public class Branch implements java.io.Serializable {
private Long id;
private Address address;
private String name;
private String contactNumber;
private Integer capacity;
private String manager;
private boolean active;
private String modifiedBy;
private Date modifiedTimestamp;
private Set<Member> members = new HashSet<Member>(0);
private Set<Staff> staff = new HashSet<Staff>(0);
Hope it helps more people.

One possible problem that can cause this behavior is that this is a lazy-fetched field. And transaction may be finished before serializing to json (depends on your code). Entity may be detached by the time Jackson gets it and getter will return null (or empty list, not sure). You can check this by making field eager-fetched or calling getter before entity will be detached (while transaction is still active).
In our case it was an container-managed EJB transaction and mapper was invoked from Jersey servlet.

Related

When I save an Entity with hibernate the foreign key is not saved always an null

I have two classes User.java and Vehicle.java with OneToMany relationship. When I post via postman a user with 2 vehicles, the data is stored correctly in the DB but the foreign key in the Vehicles table is stored always as null.
User.java
#Entity
#Table(name = "users", schema = "vehicleproject")
public class User {
#Id
#Column(name = "user_id", nullable = false)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name = "email")
private String email;
#Column(name = "password")
private String password;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
#Column(name = "address")
private String address;
#Column(name = "afm")
private int afm;
#Column(name = "role_id")
private UserType type;
#OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<Vehicle> vehicles = new ArrayList<>();
public User(){}
public User(long id, String email, String password, String firstName, String lastName, String address, int afm, UserType type, List<Vehicle> vehicles) {
this.id = id;
this.email = email;
this.password = password;
this.firstName = firstName;
this.lastName = lastName;
this.address = address;
this.afm = afm;
this.type = type;
this.vehicles = vehicles;
}
public User(long id, String email, String password, String firstName, String lastName, String address, int afm, UserType type) {
this.id = id;
this.email = email;
this.password = password;
this.firstName = firstName;
this.lastName = lastName;
this.address = address;
this.afm = afm;
this.type = type;
this.vehicles = new ArrayList<>();
}
public User(String email, String password, String firstName, String lastName, String address, int afm, UserType type, List<Vehicle> vehicles) {
this.email = email;
this.password = password;
this.firstName = firstName;
this.lastName = lastName;
this.address = address;
this.afm = afm;
this.type = type;
this.vehicles = vehicles;
}
public List<Vehicle> getVehicles() {
return vehicles;
}
public void setVehicles(List<Vehicle> vehicles) {
this.vehicles = vehicles;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public int getAfm() {
return afm;
}
public void setAfm(int afm) {
this.afm = afm;
}
public UserType getType() {
return type;
}
public void setType(UserType type) {
this.type = type;
}
public void addVehicleToList(Vehicle vehicle){
this.vehicles.add(vehicle);
}
public void removeVehicleFromUserList(Vehicle vehicle){
this.vehicles.remove(vehicle);
}
}
Vehicle.java
#Entity
#Table(name = "vehicles", schema = "vehicleproject")
public class Vehicle {
#Id
#Column(name = "vehicle_id", nullable = false)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name = "brand")
private String brand;
#Column(name = "model")
private String model;
#Column(name = "creation_date")
private LocalDate creationDate;
#Column(name = "color")
private String color;
#JsonIgnore
#ManyToOne
#JoinColumn(name = "user_id", referencedColumnName = "user_id")
private User user;
#Column(name = "plate_number")
private String plateNumber;
public Vehicle(){
}
public Vehicle(long id, String brand, String model, LocalDate creationDate, String color, User user, String plateNumber) {
this.id = id;
this.brand = brand;
this.model = model;
this.creationDate = creationDate;
this.color = color;
this.user = user;
this.plateNumber = plateNumber;
}
public Vehicle(String brand, String model, LocalDate creationDate, String color, User user, String plateNumber) {
this.brand = brand;
this.model = model;
this.creationDate = creationDate;
this.color = color;
this.user = user;
this.plateNumber = plateNumber;
}
public Vehicle(long id, String brand, String model, LocalDate creationDate, String color, String plateNumber) {
this.id = id;
this.brand = brand;
this.model = model;
this.creationDate = creationDate;
this.color = color;
this.plateNumber = plateNumber;
}
public String getPlateNumber() {
return plateNumber;
}
public void setPlateNumber(String plateNumber) {
this.plateNumber = plateNumber;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
public LocalDate getCreationDate() {
return creationDate;
}
public void setCreationDate(LocalDate creationDate) {
this.creationDate = creationDate;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
#JsonIgnore
public User getUser() {
return user;
}
#JsonProperty
public void setUser(User user) {
this.user = user;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Vehicle vehicle = (Vehicle) o;
return id == vehicle.id;
}
#Override
public int hashCode() {
return Objects.hash(id);
}
}
My json payload is:
{
"email": "new#player7.com",
"password": "newplayer2",
"firstName": "ithList",
"lastName": "Constructor",
"address": "Ermou 20",
"afm": 1005733537,
"type": "USER",
"vehicles": [
{
"brand": "MASSERATI",
"model": "GOD",
"creationDate": "2015-05-05",
"color": "WHITE",
"plateNumber": "Amm2421"
},
{
"brand": "Toyota",
"model": "Corolla",
"creationDate": "2015-05-05",
"color": "WHITE",
"plateNumber": "Fmmf2421"
}
]
}
I see this in my spring boot app it inserts all the data for User generating an Id for the new User.
It also inserts all the data for vehicles generating new Ids for Vehicles but in the FK column in Vehicles it inserts null always:
2020-07-12 15:55:20.169 TRACE 14700 --- [nio-8080-exec-2] o.h.type.descriptor.sql.BasicBinder : binding parameter [6] as [BIGINT] - [null]
RestController method for inserting a User:
#PostMapping
#ResponseStatus(HttpStatus.CREATED)
public User insert(#RequestBody User user) {
return userService.save(user);
}
#KavithakaranKanapathippillai the solution that you proposed is working!
"add this user.getVehicles().forEach(vehicle -> vehicle.setUser(user)); before return userService.save(user);"
But I cannot Understand since it is a Json with vehcles inside the User Object why it is not working directly?
Try to remove #Json from methods getUser and setUser and from field User user, and add to your json the user id:
"user": {"id" = 1}

An issue with getting user roles in a Spring Rest application

I have a RestController class with the following :
#RestController
public class UserRestController
{
#Autowired
UserService userService;
#Autowired
private SecurityService securityService;
#Autowired
private UserValidator userValidator;
// Get a Single User
#GetMapping("/api/users/{id}")
public User getUserById(#PathVariable(value = "id") Long userId) {
return userService.getUserById(userId);
}
This is getUserById function in UserService :
public User getUserById(#PathVariable(value = "id") Long userId) {
return userRepository.findById(userId).orElseThrow(() -> new ResourceNotFoundException("User", "id", userId));
}
This is the result of a GET request on localhost:8080/api/users/11 :
{
"id": 11,
"name": null,
"email": null,
"password": "$2a$10$HykDWcHU3vO9YAcdXiWieua9YyYMkwrNIk7WgpmVzVwENb71fDCsW",
"status": null,
"tel": null,
"confirmation": null,
"birth_date": null,
"createdAt": "2018-05-22T09:09:12.000+0000",
"updatedAt": "2018-05-22T09:09:12.000+0000",
"username": "ouissal#gmail.com"
}
This is the result of a GET request on localhost:8080/users/11
{
"name": null,
"email": null,
"password": "$2a$10$HykDWcHU3vO9YAcdXiWieua9YyYMkwrNIk7WgpmVzVwENb71fDCsW",
"status": null,
"tel": null,
"confirmation": null,
"birth_date": null,
"createdAt": "2018-05-22T09:09:12.000+0000",
"updatedAt": "2018-05-22T09:09:12.000+0000",
"username": "ouissal#gmail.com",
"_links": {
"self": {
"href": "http://localhost:8080/users/11"
},
"user": {
"href": "http://localhost:8080/users/11"
},
"roles": {
"href": "http://localhost:8080/users/11/roles"
}
}
}
I do not have anything mapped for /users in my controller, how can I get the roles using my controller?
edit
This is my User class :
#Entity
#Table(name = "user")
#EntityListeners(AuditingEntityListener.class)
#JsonIgnoreProperties(value = {"createdAt", "updatedAt"},
allowGetters = true)
public class User implements Serializable{
private static final long serialVersionUID = 1L;
public User() {
super();
// TODO Auto-generated constructor stub
}
#Id
#Column(name = "user_id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name = "user_name")
//#NotBlank
private String name;
#Column(name = "user_email")
//#NotBlank
private String email;
#Column(name = "user_password")
#NotBlank
private String password;
#Column(name = "user_status")
private String status;
#Column(name = "user_tel")
private String tel;
#Column(name = "user_confirmation")
private String confirmation;
#Column(name = "user_birth_date")
#Temporal(TemporalType.DATE)
private Date birth_date;
#Column(nullable = false, updatable = false)
#Temporal(TemporalType.TIMESTAMP)
#CreatedDate
private Date createdAt;
#Column(nullable = false)
#Temporal(TemporalType.TIMESTAMP)
#LastModifiedDate
private Date updatedAt;
#JsonBackReference
#ManyToMany
#JoinTable(name = "user_role", joinColumns = #JoinColumn(name = "user_id"), inverseJoinColumns = #JoinColumn(name = "role_id"))
private Set<Role> roles;
#Column(name = "username")
#NotBlank
private String username;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getTel() {
return tel;
}
public void setTel(String tel) {
this.tel = tel;
}
public String getConfirmation() {
return confirmation;
}
public void setConfirmation(String confirmation) {
this.confirmation = confirmation;
}
public Date getBirth_date() {
return birth_date;
}
public void setBirth_date(Date birth_date) {
this.birth_date = birth_date;
}
public Date getCreatedAt() {
return createdAt;
}
public Date getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(Date updatedAt) {
this.updatedAt = updatedAt;
}
public Set<Role> getRoles() {
return roles;
}
public void setRoles(Set<Role> roles) {
this.roles = roles;
}
}
and this is my role class :
#Entity
#Table(name = "role")
public class Role {
#Id
#Column(name = "role_id")
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "role_name")
private String name;
#ManyToMany(mappedBy = "roles")
#JsonManagedReference
private Set<User> users;
public Role() {
super();
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<User> getUsers() {
return users;
}
public void setUsers(Set<User> users) {
this.users = users;
}
}
Properties annotated with JsonBackReference will not be included in your serialized content.
To include the roles swap the JsonBackReference and JsonManagedReference annotations in Role and User.
This will include Roles into User but not the other way round.
For more information you can check this answer

JPA- Compound Primary Key using #EmbededId

Goal: To impolement an #Entity where the id is a compound primary key using #EmbededId.
Problem: Based on my current implementation, I am getting the following result:
[
{
"id": 1,
"name": "Recipe 1",
"instruction": "Test Instruction",
"note": "Note 1",
"show": true,
"createDate": null,
"modify_date": null,
"ingredient": [
{},
{}
]
}
]
but I want to have this:
[
{
"id": 1,
"name": "Recipe 1",
"instruction": "Test Instruction",
"note": "Note 1",
"show": true,
"createDate": null,
"modify_date": null,
"ingredient": [
{ingredient_id: 1,
amount: 10},
{ingredient_id: 2,
amount: 20}
]
}
]
Can someone please help me too see where I have done wrong in my recipeIngredient class? Thanks in advance.
The following are my implementation:
The schema:
RecipeIngredientId.java
#Embeddable
public class RecipeIngredientId implements Serializable {
#Column(name = "recipe_id", nullable = false)
private int recipeId;
#Column(name = "ingredient_id", nullable = false)
private int ingredientId;
public RecipeIngredientId() {}
public RecipeIngredientId(int recipeId, int ingredientId) {
this.recipeId = recipeId;
this.ingredientId = ingredientId;
}
}
RecipeIngredient.java
#Entity
#Table(name = "recipe_ingredient")
public class RecipeIngredient implements Serializable
{
#EmbeddedId
private RecipeIngredientId id;
#ManyToOne
#JoinColumn(name="ingredient_id", insertable = false, updatable = false)
private Ingredient ingredient;
#ManyToOne
#JoinColumn(name = "recipe_id", insertable = false, updatable = false)
private Recipe recipe;
private double amount;
public RecipeIngredient() {}
public RecipeIngredient(Recipe recipe, Ingredient ingredient, double amount){
this.recipe = recipe;
this.ingredient = ingredient;
this.amount = amount;
}
}
Recipe.java:
#Entity
public class Recipe {
private int id;
#NotNull
private String name;
private String instruction;
private String note;
#NotNull
private boolean show;
#CreationTimestamp
#Temporal(TemporalType.DATE)
#Column(name = "create_date")
private Date createDate;
#UpdateTimestamp
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "modify_date")
private Date modify_date;
private Set<RecipeIngredient> recipeIngredients;
public Recipe() {}
public Recipe(String name, String instruction, String note, boolean show) {
this.name = name;
this.instruction = instruction;
this.note = note;
this.show = show;
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getInstruction() {
return instruction;
}
public void setInstruction(String instruction) {
this.instruction = instruction;
}
public String getNote() {
return note;
}
public void setNote(String note) {
this.note = note;
}
public boolean isShow() {
return show;
}
public void setShow(boolean show) {
this.show = show;
}
public Date getCreateDate() {
return createDate;
}
public void setCreateDate(Date createDate) {
this.createDate = createDate;
}
public Date getModify_date() {
return modify_date;
}
public void setModify_date(Date modify_date) {
this.modify_date = modify_date;
}
#OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL)
public Set<RecipeIngredient> getIngredient() {
return recipeIngredients;
}
public void setIngredient(Set<RecipeIngredient> recipeIngredients) {
this.recipeIngredients = recipeIngredients;
}
}
Ingredient.java
#Entity
public class Ingredient {
private int id;
#NotNull
#Column(unique=true)
private String name;
private Set<RecipeIngredient> recipeIngredients;
public Ingredient() {}
public Ingredient(String name) {
this.name = name;
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#OneToMany(mappedBy = "ingredient", cascade = CascadeType.ALL)
public Set<RecipeIngredient> getRecipeIngredients() {
return recipeIngredients;
}
public void setRecipeIngredients(Set<RecipeIngredient> recipeIngredients) {
this.recipeIngredients = recipeIngredients;
}
}
This is a case of "derived identity". RecipeIngredient should look like this:
#Entity
#Table(name = "recipe_ingredient")
public class RecipeIngredient implements Serializable
{
#EmbeddedId
private RecipeIngredientId id;
#MapsId("ingredientId") // maps ingredientId attribute of embedded id
#ManyToOne
#JoinColumn(name="ingredient_id", insertable = false, updatable = false)
private Ingredient ingredient;
#MapsId("recipeId") // maps recipeId attribute of embedded id
#ManyToOne
#JoinColumn(name = "recipe_id", insertable = false, updatable = false)
private Recipe recipe;
private double amount;
public RecipeIngredient() {}
public RecipeIngredient(Recipe recipe, Ingredient ingredient, double amount){
this.recipe = recipe;
this.ingredient = ingredient;
this.amount = amount;
}
}
Note the MapsId annotations on the two fields whose primary keys make up the entity's composite key.
Derived identities are discussed in the JPA 2.1 spec in section 2.4.1.

Hibernate: Many to many mapping exception

I am stuck up with mapping exception. I have many to many relationship between Employee and Role. Here is the code.
Role class
#Entity
#Table(name = "role", catalog = "app")
public class Role implements java.io.Serializable {
#GeneratedValue(strategy = IDENTITY)
#Column(name = "roleId", unique = true, nullable = false)
private Integer roleId;
#Column(name = "title", nullable = false, length = 50)
private String title;
#ManyToMany(fetch = FetchType.LAZY, mappedBy = "roles")
private Set<Employee> employees = new HashSet<Employee>(0);
public Role() {
}
public Role(String title) {
this.title = title;
}
public Role(String title, Set<Employee> employees) {
this.title = title;
this.employees = employees;
}
public Integer getRoleId() {
return this.roleId;
}
public void setRoleId(Integer roleId) {
this.roleId = roleId;
}
public String getTitle() {
return this.title;
}
public void setTitle(String title) {
this.title = title;
}
public Set<Employee> getEmployees() {
return this.employees;
}
public void setEmployees(Set<Employee> employees) {
this.employees = employees;
}
}
and Employee class
#Entity
#Table(name = "employee", catalog = "app", uniqueConstraints = #UniqueConstraint(columnNames = "email"))
public class Employee implements java.io.Serializable {
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "empId", unique = true, nullable = false)
private Integer empId;
#Column(name = "name", nullable = false, length = 100)
private String name;
#Column(name = "email", unique = true, nullable = false, length = 50)
private String email;
#Column(name = "phone", nullable = false, length = 11)
private String phone;
#Column(name = "ip", nullable = false, length = 20)
private String ip;
#Column(name = "password", nullable = false, length = 200)
private String password;
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "employee_role", catalog = "app", joinColumns = {
#JoinColumn(name = "empId", nullable = false, updatable = false) }, inverseJoinColumns = {
#JoinColumn(name = "roleId", nullable = false, updatable = false) })
private Set<Role> roles = new HashSet<Role>(0);
public Employee() {
}
public Integer getEmpId() {
return this.empId;
}
public void setEmpId(Integer empId) {
this.empId = empId;
}
public City getCity() {
return this.city;
}
public void setCity(City city) {
this.city = city;
}
public Posts getPosts() {
return this.posts;
}
public void setPosts(Posts posts) {
this.posts = posts;
}
public Teams getTeams() {
return this.teams;
}
public void setTeams(Teams teams) {
this.teams = teams;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return this.email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPhone() {
return this.phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getIp() {
return this.ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public String getPassword() {
return this.password;
}
public void setPassword(String password) {
this.password = password;
}
public Set<Role> getRoles() {
return this.roles;
}
public void setRoles(Set<Role> roles) {
this.roles = roles;
}
}
Here is the exception. Please also explain the exception
org.hibernate.AnnotationException: mappedBy reference an unknown target entity property: org.company.app.models.Role.employees in org.company.app.models.Employee.roles
what is wrong?
It looks you miss #Id anotation for Role class
#Id
#Column(name = "roleId", unique = true, nullable = false)
private Integer roleId;

Use join and subquery with criteria in hibernate

I searched lot. But can't find solution for my case. i want create hibernate criteria for following query.
SELECT * FROM patient as p1 LEFT OUTER JOIN (SELECT * FROM patient_caller_admin_map WHERE caller_admin_id='1') as pca ON p1.patient_id=pca.patient_id;
i went through the DetachedCriteria , Criteria and created the following things. But don't know how to use LEFT_JOIN by joining both.
DetachedCriteria inner=DetachedCriteria.forClass(PatientCallerAdminMap.class, "patientCallerAdmin");
Criteria cr1=this.sessionFactory.getCurrentSession().createCriteria(Patient.class,"patient");
PatientCallerAdminMap Entity:
/**
* PatientCallerAdminMap generated by hbm2java
*/
#Entity
#Table(name = "patient_caller_admin_map", catalog = "test")
public class PatientCallerAdminMap implements java.io.Serializable {
private PatientCallerAdminMapId id;
private CallerAdmin callerAdmin;
private Caller caller;
private Patient patient;
private String notes;
private Integer isArchived;
private Integer patientStatus;
private Set<CallLog> callLogs = new HashSet<CallLog>(0);
private Set<CallLog> callLogs_1 = new HashSet<CallLog>(0);
public PatientCallerAdminMap() {
}
public PatientCallerAdminMap(PatientCallerAdminMapId id,
CallerAdmin callerAdmin, Patient patient) {
this.id = id;
this.callerAdmin = callerAdmin;
this.patient = patient;
}
public PatientCallerAdminMap(PatientCallerAdminMapId id,
CallerAdmin callerAdmin, Caller caller, Patient patient,
String notes, Integer isArchived, Integer patientStatus,
Set<CallLog> callLogs, Set<CallLog> callLogs_1) {
this.id = id;
this.callerAdmin = callerAdmin;
this.caller = caller;
this.patient = patient;
this.notes = notes;
this.isArchived = isArchived;
this.patientStatus = patientStatus;
this.callLogs = callLogs;
this.callLogs_1 = callLogs_1;
}
#EmbeddedId
#AttributeOverrides({
#AttributeOverride(name = "patientId", column = #Column(name = "patient_id", nullable = false)),
#AttributeOverride(name = "callerAdminId", column = #Column(name = "caller_admin_id", nullable = false)) })
public PatientCallerAdminMapId getId() {
return this.id;
}
public void setId(PatientCallerAdminMapId id) {
this.id = id;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "caller_admin_id", nullable = false, insertable = false, updatable = false)
public CallerAdmin getCallerAdmin() {
return this.callerAdmin;
}
public void setCallerAdmin(CallerAdmin callerAdmin) {
this.callerAdmin = callerAdmin;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "caller_id")
public Caller getCaller() {
return this.caller;
}
public void setCaller(Caller caller) {
this.caller = caller;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "patient_id", nullable = false, insertable = false, updatable = false)
public Patient getPatient() {
return this.patient;
}
public void setPatient(Patient patient) {
this.patient = patient;
}
#Column(name = "notes", length = 600)
public String getNotes() {
return this.notes;
}
public void setNotes(String notes) {
this.notes = notes;
}
#Column(name = "is_archived")
public Integer getIsArchived() {
return this.isArchived;
}
public void setIsArchived(Integer isArchived) {
this.isArchived = isArchived;
}
#Column(name = "patient_status")
public Integer getPatientStatus() {
return this.patientStatus;
}
public void setPatientStatus(Integer patientStatus) {
this.patientStatus = patientStatus;
}
#OneToMany(fetch = FetchType.LAZY, mappedBy = "patientCallerAdminMap")
public Set<CallLog> getCallLogs() {
return this.callLogs;
}
public void setCallLogs(Set<CallLog> callLogs) {
this.callLogs = callLogs;
}
#OneToMany(fetch = FetchType.LAZY, mappedBy = "patientCallerAdminMap")
public Set<CallLog> getCallLogs_1() {
return this.callLogs_1;
}
public void setCallLogs_1(Set<CallLog> callLogs_1) {
this.callLogs_1 = callLogs_1;
}
}
Patient Entity Class:
#Entity
#Table(name = "patient", catalog = "test")
public class Patient implements java.io.Serializable {
private String patientId;
private String addedDate;
private String name;
private String dateOfBirth;
private String gender;
private String address;
private String phoneNumber;
private Integer tier;
private Integer patientStatus;
private Integer status;
private Set<PatientCallerAdminMap> patientCallerAdminMaps = new HashSet<PatientCallerAdminMap>(
0);
public Patient() {
}
public Patient(String patientId) {
this.patientId = patientId;
}
public Patient(String patientId,String addedDate, String timeOfCrash,
String name, String dateOfBirth, String gender,
String address,
String phoneNumber,Integer tier, Integer patientStatus,
Integer status,
Set<PatientCallerAdminMap> patientCallerAdminMaps,
) {
this.patientId = patientId;
this.addedDate = addedDate;
this.name = name;
this.dateOfBirth = dateOfBirth;
this.gender = gender;
this.address = address;
this.phoneNumber = phoneNumber;
this.tier=tier;
this.patientStatus = patientStatus;
this.status = status;
this.patientCallerAdminMaps = patientCallerAdminMaps;
}
#Id
#Column(name = "patient_id", unique = true, nullable = false)
public String getPatientId() {
return this.patientId;
}
public void setPatientId(String patientId) {
this.patientId = patientId;
}
#Column(name = "added_date", length = 45)
public String getAddedDate() {
return addedDate;
}
public void setAddedDate(String addedDate) {
this.addedDate = addedDate;
}
#Column(name = "name", length = 100)
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
#Column(name = "date_of_birth", length = 45)
public String getDateOfBirth() {
return this.dateOfBirth;
}
public void setDateOfBirth(String dateOfBirth) {
this.dateOfBirth = dateOfBirth;
}
#Column(name = "gender", length = 5)
public String getGender() {
return this.gender;
}
public void setGender(String gender) {
this.gender = gender;
}
#Column(name = "address", length = 200)
public String getAddress() {
return this.address;
}
public void setAddress(String address) {
this.address = address;
}
#Column(name = "phone_number", length = 20)
public String getPhoneNumber() {
return this.phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
#Column(name = "tier")
public Integer getTier() {
return this.tier;
}
public void setTier(Integer tier) {
this.tier = tier;
}
#Column(name = "patient_status")
public Integer getPatientStatus() {
return this.patientStatus;
}
public void setPatientStatus(Integer patientStatus) {
this.patientStatus = patientStatus;
}
#Column(name = "status")
public Integer getStatus() {
return this.status;
}
public void setStatus(Integer status) {
this.status = status;
}
#OneToMany(fetch = FetchType.LAZY, mappedBy = "patient")
public Set<PatientCallerAdminMap> getPatientCallerAdminMaps() {
return this.patientCallerAdminMaps;
}
public void setPatientCallerAdminMaps(
Set<PatientCallerAdminMap> patientCallerAdminMaps) {
this.patientCallerAdminMaps = patientCallerAdminMaps;
}
}
Please help to solve this.
Maybe you can achieve this without using subquery so the query become simpler :
Criteria cr1=this.sessionFactory.getCurrentSession().createCriteria(Patient.class,"patient");
cr2=cr1.createCriteria("patientCallerAdminMaps ",CriteriaSpecification.LEFT_JOIN);
cr3= cr2.createCriteria("callerAdmin",CriteriaSpecification.LEFT_JOIN);
cr3.add(Restrictions.eq("id", "1"));
For the "select *" you can't do it with criteria. This criteria will return a list of Patient entity.
If really want * you will have to add alias on subcriteria and use Projection to select explicitly the fields that you want

Categories