JAXB. Advise correct annotations - java

There is a structure. I want to link the three entities in this way: the Company should contain id, name of company and the list of Departments, each Department has a list of Workers, id and name of department. Each worker has name, id.
+Company
-int companyId
-String companyName
-Set<Department> listOfDepartments = new HashSet<Department>();
+Department
-int departmentId
-String departmentName
-Set<Worker> listOfWorkers = new HashSet<Worker>();
+Worker
-int workerId
-String workerName
#XmlRootElement(name="Company")
#XmlAccessorType(XmlAccessType.FIELD)
#Entity
public class Company {
#XmlAttribute(name = "id")
#Id #GeneratedValue(strategy = GenerationType.AUTO)
private int companyId;
#XmlElement(name = "companyName")
private String companyName;
#XmlElement(name="Department")
#OneToMany(mappedBy = "company", cascade=CascadeType.PERSIST, fetch = FetchType.EAGER)
private Set<Department> listOfDepartments = new HashSet<Department>();
#XmlRootElement(name="Department")
#XmlAccessorType(XmlAccessType.FIELD)
#Entity
public class Department {
#XmlAttribute(name = "id")
#Id #GeneratedValue(strategy = GenerationType.AUTO)
private int idDepartment;
#XmlElement(name = "departmentName")
private String departmentName;
#ManyToOne()
#JoinColumn(name="companyId")
private Company company;
#XmlElement(name="Worker")
#OneToMany(mappedBy = "department", cascade = CascadeType.PERSIST, fetch = FetchType.EAGER)
private Set<Worker> listOfWorkers = new HashSet<Worker>();
#XmlRootElement(name="Worker")
#Entity
public class Worker {
#Id #GeneratedValue(strategy = GenerationType.AUTO)
private int idWorker;
private String workerName;
#ManyToOne
#JoinColumn(name="departmentId")
private Department department;
ERROR:
A cycle is detected in the object graph. This will cause infinitely deep XML: ru.eldarkaa.dto.Company#d1e43ed ->
ru.eldarkaa.dto.Department#6e55f58 -> ru.eldarkaa.dto.Company#d1e43ed]
How to avoid this cycle?

You have a bidirectional relationship in your model. To solve it you could do the following:
Solutions Using Standard JAXB Metadata
1 - #XmlTransient
You can map one direction of the relationship with #XmlTransient. This will cause the field/property not to marshal preventing the infinite loop. This field/property also won't unmarshal meaning that you will need to populate it yourself.
2 - #XmlID/#XmlIDREF
#XmlIDREF is how you map shared references with JAXB you can use this to map the back-pointer relationship.
http://blog.bdoughan.com/2010/10/jaxb-and-shared-references-xmlid-and.html
Solutions Leveraging EclipseLink JAXB (MOXy) Extensions
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
You can use MOXy's #XmlInverseReference extension for this use case. It acts like #XmlTransient on the marshal operation, but will still populate the value on the unmarshal.
http://blog.bdoughan.com/2010/07/jpa-entities-to-xml-bidirectional.html
http://blog.bdoughan.com/2013/03/moxys-xmlinversereference-is-now-truly.html

Related

Spring Boot: How to create similar another entity by using existing entity class

I'm developing one simple app where where I have an one entity class class Employee. And now I want to create/copy new similar entity called ActiveEmployees from existing Employee. I want to add functionality that If I hit the new api endpoint ->POST: http://locahost:8080/api/employee/active/john -> So, it should save existing Employee John Record in the new table active_employees with the all Table data.
#Entity
#Table(name="employee")
public class Employee{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column
#NotNull
private String firstNname;
#Column
#NotNull
private String lastNname;
#Column
#NotNull
private String department;
#JsonManagedReference
#OneToOne(fetch = FetchType.LAZY,
mappedBy = "employee",
cascade = CascadeType.ALL,
orphanRemoval = true)
ActiveEmployee activeEmployee;
... Constructor, getters and setters
}
#Entity
#Table(name="active_employees")
public class ActiveEmployees {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#JsonBackReference
#OneToOne(cascade = CascadeType.ALL,fetch = FetchType.LAZY)
#JoinColumn(name = "employee_id")
private Employee employee;
}
I think you should use inheritance mapping in hibernate instead having two table with same fields. There are multiple strategies. Check and use best one which fits your requirement.
Read the tutorial here https://www.javatpoint.com/hibernate-inheritance-mapping-tutorial
You can use inhertiance with #MappedSuperclass. But if I will design this application I will add boolean field "active" to Employee class.

Springboot JPA Hibernate CascadeType confusion

How to make a column cascade enable only for insertion not update and delete.
Here are two class. There are #ManyToOne relationship between Qualification and Department. At the time of insertion I want to insert new department with qualification. But at the time of update of qualification I don't want to update department. How can I configure that??
#Table(name = "department")
public class Department {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
}
#Table(name = "qualification")
public class Qualification implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
Integer id;
private Integer passingYear;
private String result;
#ManyToOne(optional = true, cascade = { CascadeType. ? }, fetch = FetchType.EAGER)
#JoinColumn(name = "department_id", referencedColumnName = "id")
private Department department;
}
CascadeType.PERSIST is what you are looking for. The various CascadeType enums essentially say, which persistence operation of EntityManager persist/merge/remove etc. should cascade to the objects of an association.

JPA one to one mapping creates multiple query when child entity is not found

I have a parent entity 'contracts' that has a one-to-one relation with another entity 'child-contract'. the interesting thing is that the mapping field ('contract_number')id not a primary key-foreign key but is rather a unique field in both the tables. Also it is possible for a contracts to not have any child contract altogether. With this configuration I have observed hibernate to generate 1 additional query every time a contracts does not have a child-contract. I filed this behavior very strange. Is there a way to stop these unnecessary query generation or have I got something wrong.
below is a piece of my code configuration.
#Data
#Entity
#Table(name = "contracts")
public class Contracts implements Serializable {
#Id
#JsonIgnore
#Column(name = "id")
private String id;
#JsonProperty("contract_number")
#Column(name = "contract_number")
private String contractNumber;
#OneToOne(fetch=FetchType.EAGER)
#Fetch(FetchMode.JOIN)
#JsonProperty("crm_contracts")
#JoinColumn(name = "contract_number", referencedColumnName = "contract_number")
private ChildContract childContract ;
}
#Data
#NoArgsConstructor
#Entity
#Table(name = "child_contract")
#BatchSize(size=1000)
public class ChildContract implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#JsonProperty("id")
#Column(name = "id")
private String id;
#JsonProperty("contract_number")
#Column(name = "contract_number")
private String contractNumber;
}
Please help.
Thank-you
You can use NamedEntityGraph to solve multiple query problem.
#NamedEntityGraph(name = "graph.Contracts.CRMContracts", attributeNodes = {
#NamedAttributeNode(value = "crmContract") })
Use this on your repository method as
#EntityGraph(value = "graph.Contracts.CRMContracts", type = EntityGraphType.FETCH)
// Your repo method in repository

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.

JPA Compound key with #EmbeddedId

In a legacy database, I have three tables: Users, Workgroups, and UsersWorkgroup. UsersWorkgroup stores what role a user has in a workgroup.
Here are the relevant code snippets:
#Entity
#Table(name = "users_workgroup")
public class UsersWorkgroup implements Serializable {
private static final long serialVersionUID = 1L;
#EmbeddedId
protected UsersWorkgroupPK usersWorkgroupPK;
#JoinColumn(name = "idworkgroup", referencedColumnName = "idworkgroup")
#ManyToOne(optional = false)
private Workgroup workgroup;
#JoinColumn(name = "user_name", referencedColumnName = "user_name")
#ManyToOne(optional = false)
private Users users;
#Column(name = "role")
private Integer role;
#Embeddable
public class UsersWorkgroupPK implements Serializable {
#Basic(optional = false)
#Column(name = "idworkgroup", insertable=false, updatable=false)
private int idworkgroup;
#Basic(optional = false)
#Column(name = "user_name", insertable=false, updatable=false)
private String userName;
#Entity
#Table(name = "workgroup")
public class Workgroup implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "idworkgroup")
private Integer idworkgroup;
#Column(name = "name")
private String name;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "idworkgroup")
private Collection<UsersWorkgroup> usersWorkgroupCollection;
And of course, problem is, it doesn't work.
Currently I get this exception:
Exception Description: An incompatible
mapping has been encountered between
[class entity.Workgroup] and [class
entity.UsersWorkgroup]. This usually
occurs when the cardinality of a
mapping does not correspond with the
cardinality of its backpointer.
Which I don't understand since OneToMany should match ManyToOne... Or is it a ManyToMany relationship? If I switch to #ManyToMany, I get this:
Exception Description: The target
entity of the relationship attribute
[workgroup] on the class [class
com.ericsson.rsg.ejb.entity.UsersWorkgroup]
cannot be determined. When not using
generics, ensure the target entity is
defined on the relationship mapping.
I'm trying to understand compound keys (embedded), but all the examples I could find have only simple columns that are not foreign keys (but that's the whole point of a compound key, isn't it?). Can the UsersWorkgroup table secretly be a join table?
Should I declare the PK class as a strict POJO class? Or should I put the #JoinColumn annotations in the PK class? How do I refer to the columns within the compound key from another table? Should I initialize the PK object in the refering class constructor, or is it not necessary?
I feel stuck completely.
First of all, I think your relation is a Many To Many, as a user can be in many groups, and a group can have many users (or I would assume so).
Second, as far as I know you have to reference both id_workgroup and user_name as JoinColumns, because they are part of the PK and a unit, so both should be referenced.
Also, I see the "equals" and "hashCode" methods missing from your embedded PK, as well as the getters/setters. I believe they are mandatory.
Your mapping looks fine except for mappedBy - it should be a property name, not a column name:
#OneToMany(cascade = CascadeType.ALL, mappedBy = "workgroup")
private Collection<UsersWorkgroup> usersWorkgroupCollection;

Categories