JAXB how to map composite key fields as siblings - java

I have the following entities defined after my database model:
#Entity
#Table(name = "PERSON")
#XmlRootElement(name = "person")
public class Person implements java.io.Serializable {
#Id
private Long personId;
private String personName;
#XmlElementWrapper(name="children")
#XmlElement(name="child")
private Set<Child> children = new HashSet<Child>(0);
// getters & setters
}
#Entity
#Table(name = "CHILD")
#XmlRootElement(name = "child")
public class Child implements java.io.Serializable {
#EmbeddedId
private ChildId id;
private Date childName;
// getters & setters
}
#Embeddable
public class ChildId implements java.io.Serializable {
#XmlTransient
private Person parentId;
private Date childBirthDate;
private int childOrder;
// getters & setters
}
In the current state of thing, when marshalling using JAXB here is what I get:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<person>
<personId>7</personId>
<personName>Foo</personName>
<children>
<child>
<childName>Bar</childName>
<id>
<childBirthDate>2001-06-22</childBirthDate>
<childOrder>1</childOrder>
</id>
</child>
</children>
</person>
What I would like to get instead is something like:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<person>
<personId>7</personId>
<personName>Foo</personName>
<children>
<child>
<childName>Bar</childName>
<childBirthDate>2001-06-22</childBirthDate>
<childOrder>1</childOrder>
</child>
</children>
</person>
Is there a way for me to get the ChildId's fields to the same level as the other Child's fields?
Please advise what my options are.

Moving comment to answer:
As multiple #Ids are possible in the same class, you could simply move the fields from the ChildId class into the Child class. Then you annotate each field with #Id (the fields which were moved from ChildId). This way you have all fields on the same level as they are in the same class, while also retaining the functionality of #EmbeddedId

Related

JPA Repository Join - Continuous Loop of data when retrieving data from DB

I have two repositories - User and Address.
User has a one to one relationship with Address and they are linked by ID. See below code snippets. When I pull data using a JPA repository, I get basically a constant loop of data.
EG:
<Data>
<User>
<id>1</id>
<name>Mary</name>
<dob>21/01/1990</dob>
<Address>
<id>1<id>
<address>123 Main Street</address>
<User>
<id>1</id>
<name>Mary</name>
<dob>21/01/1990</dob>
<Address>
<id>1<id>
<address>123 Main Street</address>
<User>
....
and so on like this causing my query to take a large amount of time to run. Is there anyway to stop the User object from being returned within the Address object? Any help would be greatly appreciated.
#Entity
#Data
#Table(name = "ADDRESS")
public class Address{
#Id
#Column()
private String id;
#Column()
private String address;
#OneToOne (fetch = FetchType.LAZY)
#JoinColumn(name = "id")
private User user;
}
#Entity
#Data
#Table(name = "User")
public class User{
#Id
#Column()
private String id;
#Column()
private String name;
#Column()
private String dob;
#OneToOne ()
#JoinColumn(name = "id")
private Address address;
}
This is normal behaviour. Serializers call getters to serialize data which are intercepted by the Hibernate Proxy loading the data even if they are lazy. To prevent this, You have to add #JsonBackReference to the address field in your User class, and #JsonManagedReference to the user field in your Address class.

JAXB referencing ChildB in ChildA inheritance

I'm trying to unmarshall the following xml:
<?xml version="1.0" encoding="UTF-8" ?>
<container>
<childs>
<child uuid="1" childB="3" attrA="specialAttrA1" />
<child uuid="2" childB="4" attrA="specialAttrA2" />
<child uuid="3" attrB="specialAttrB1" />
<child uuid="4" attrB="specialAttrB2" />
</childs>
</container>
As far as I can see this scenario contains two major problems:
modelling the object hierachy
referencing a subclass from within another subclass
The object hierachy consists of ChildA and ChildB which are subclasses of Parent (See the sourcecode below).
In the end the unmarshalled objects look good besides the references 'childB' which are null.
I couldn't find any example for this scenario. Debugging the source below questions me why the FlatParent object passed to the unmarshal-method of the Adapter doesn't contain childB.
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Container {
#XmlElementWrapper(name = "childs")
#XmlElement(name = "child")
private List<Parent> childs = new ArrayList<>();
}
#XmlRootElement
#XmlAccessorType(value = XmlAccessType.FIELD)
#XmlJavaTypeAdapter(value = ParentAdapter.class, type = Parent.class)
public abstract class Parent {
#XmlID
#XmlAttribute
private String uuid;
}
#XmlRootElement
#XmlAccessorType(value = XmlAccessType.FIELD)
public class ChildA extends Parent{
#XmlAttribute
private String attrA;
#XmlIDREF
#XmlAttribute
private ChildB childB;
}
#XmlRootElement
#XmlAccessorType(value = XmlAccessType.FIELD)
public class ChildB extends Parent{
#XmlAttribute
private String attrB;
}
The corresponding Adapter looks like:
public class ParentAdapter extends XmlAdapter<FlatParent, Parent> {
public Parent unmarshal(FlatParent fp) {
Parent p = null;
if (fp != null){
if (fp.getAttrA() != null){
p = new ChildA();
((ChildA) p).setAttrA(fp.getAttrA());
((ChildA) p).setChildB(fp.getChildB());
} else {
p = new ChildB();
((ChildB) p).setAttrB(fp.getAttrB());
}
p.setUuid(fp.getUuid());
}
return null;
}
...
}
And the containing FlatParent:
#XmlRootElement
#XmlAccessorType(value = XmlAccessType.FIELD)
public class FlatParent {
#XmlID
#XmlAttribute
private String uuid;
#XmlAttribute
private String attrA;
#XmlAttribute
private String attrB;
#XmlIDREF
#XmlElement
private ChildB childB;
}
Just to be completely here is the part of the Testing class also:
#Test
public void testImportXml() throws JAXBException {
JAXBContext jc = JAXBContext.newInstance(Container.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
Container cont = (Container) unmarshaller.unmarshal(new File(FILEPATH));
You have two option I think:
1, Use an unmarshaller-listener, the drawback is that you have to read the XML twice and update your references manually after every involved object were unmarshalled
2, Read your XML as is, with numbers and convert them after unmarshalling to another pojos using a java bean mapper, e.g. MapStruct (In mapstruct, the fields with the same name mapped automatically and you can do this reference mapping in a decorator subclass)

Hibernate Annotation on parent fields

I have a class that is a POJO with no special Hibernate annotations or information, like so:
public class Parent{
Long id;
String foo;
String bar;
/* ... getters and setters, toString(), etc... */
}
I would like to create a child class that has the Hibernate annotations on it. The idea is that the first class will not have any dependencies on it, and the second will have all of the JPA/Hibernate specific stuff. How can I do that without re-creating all the fields in the parent? I would like to put Hibernate annotations on the class
#Entity
public class PersistentChild extends Parent{
// ????
}
You can use the #MappedSuperclass annotation on the POJO, then add the other annotations, as it were a normal JPA entity. But in this case, the annotations will only affect the entity classes, which are inheriting from it. Example:
#MappedSuperclass
public class Parent implements Serializable {
#Id
Long id;
#Column(name = "foo", required = true)
String foo;
#Column(name = "bar", required = false)
String bar;
/* ... getters and setters, toString(), etc... */
}
If you really do not want to modify the superclass, you can use a mapping file:
<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm
http://java.sun.com/xml/ns/persistence/orm_1_0.xsd" version="1.0">
<mapped-superclass class="Parent">
<!-- add your mapping here -->
</mapped-superclass>
</entity-mappings>
Alternative approach
Also, you can just add the #MappedSuperclass annotation, then define all properties like this:
#Entity
#AttributeOverrides({
#AttributeOverride(name = "foo", column=#Column(name = "foo", required = true)),
#AttributeOverride(name = "bar", column=#Column(name = "bar", required = false))
})
public class PersistentChild extends Parent {
#Id #GeneratedValue
Long id;
}
If you want to externalize the mapping you have to use xml-mapping file.

jaxb reference xmlID between xml files

I tried the approach in this post
However I am getting a
>
1 counts of IllegalAnnotationExceptions
XmlIDREF property is referencing a type "java.lang.String" that doesn't have an XmlID property.
this problem is related to the following location:
at private externalReferences.Department
externalReferences.Employee.department
at externalReferences.Employee
at private java.util.List externalReferences.Company.employees
at externalReferences.Company
The two xml Files are the following:
employee.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<company>
<employeeList>
<employee name="Jane Doe" id="1">
<department>1</department>
</employee>
<employee name="John Smith" id="2">
<department>2</department>
</employee>
<employee name="Anne Jones" id="3">
<department>3</department>
</employee>
</employeeList>
</company>
department.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<departmentList>
<departmentList>
<department name="Dev" id="1"/>
<department name="Sales" id="2"/>
<department name="Research" id="3"/>
</departmentList>
</departmentList>
The employee.xml references the department and I want to point to the right department objects when unmarshalling employee.xml.
Classes are as follows:
Company.java
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Company {
#XmlElementWrapper(name = "employeeList")
#XmlElement(name="employee")
private List<Employee> employees;
#XmlElementWrapper(name = "departmentList")
#XmlElement(name="department")
private List<Department> departments;
public Company() {
employees = new ArrayList<Employee>();
departments = new ArrayList<Department>();
}
...
}
Employee.java
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Employee {
#XmlAttribute
#XmlID
private String id;
public String getId() {
return id;
}
#XmlIDREF
private Employee manager;
#XmlJavaTypeAdapter(EmpAdapter.class)
#XmlIDREF
private Department department;
}
Department.java
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Department {
#XmlAttribute
#XmlID
private String id;
...
}
DepartmentList.java
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class DepartmentList {
#XmlElementWrapper(name = "departmentList")
#XmlElement(name="department")
private List<Department> departments;
Then I run the following in Main
JAXBContext jc = JAXBContext.newInstance(DepartmentList.class); Unmarshaller unmarshaller = jc.createUnmarshaller();
DepartmentList depList = (DepartmentList) unmarshaller.unmarshal(new FileReader(DepRef));
EmpAdapter adapter = new EmpAdapter();
for(Department dep : depList.getDepartments()) {
adapter.getDepList().put(dep.getId(), dep);
}
JAXBContext jc2 = JAXBContext.newInstance(Company.class);
Unmarshaller unmarshaller2 = jc2.createUnmarshaller();
unmarshaller2.setAdapter(adapter);
Company company2 = (Company) unmarshaller2.unmarshal(new FileReader(empRef));
I feel that having one XMLIDREF refer to employee id and the other XMLIDREF refer to department id is part of the problem. But that is required since the manager field references other employee objects.
Can someone please help me with this. Thank you
The problem arises from class Company that corresponds to an XML document containing employees and departments. However, you've got two separate documents. Apparently you want one final class containing both lists.
(1) You could define a class EmployeeList for employees only, similar to the one for departments (DepartmentList). This will still let you write an application class Company into which you set the references for both lists.
(2) Change the annotation for Company.departments
#XmlTransient
private List<Department> departments;
marshal like you do now, and set the List with the reference you have from unmarshalling the corresponding XML into the returned object.

JPA Relation between classes, referring to their Interfaces

I have two classes with their respective interfaces between which I want to create a JPA #OneToOne Relation. This fails with [class EmployeeImpl] uses a non-entity [class Adress] as target entity in the relationship attribute [field adress].
First Interface / Class:
public interface Employee {
public long getId();
public Adress getAdress();
public void setAdress(Adress adress);
}
#Entity(name = "EmployeeImpl")
#Table(name = "EmployeeImpl")
public class EmployeeImpl implements Employee {
#Id
#Column(name = "employeeId")
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#OneToOne(cascade = CascadeType.PERSIST)
private Adress adress;
// snip, getters and setters
}
Second Interface / Class:
public interface Adress {
public long getId();
public String getStreet();
public void setStreet(String street);
}
#Entity(name = "AdressImpl")
#Table(name = "AdressImpl")
public class AdressImpl implements Adress {
#Id
#Column(name = "AdressId")
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Column(name = "Street")
private String street;
// Snip getters and setters
}
The persistence.xml looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0"
xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="employee"
transaction-type="RESOURCE_LOCAL">
<class>EmployeeImpl</class>
<class>AdressImpl</class>
<properties>
<property name="eclipselink.create-ddl-jdbc-file-name"
value="create-matterhorn-employee.jdbc" />
<property name="eclipselink.drop-ddl-jdbc-file-name"
value="drop-matterhorn-employee.jdbc" />
</properties>
</persistence-unit>
</persistence>
I shortened out package names and imports and such. Exception occurs when trying to create the EntityManagerFactory (where you hand over the persistence unit). I am using eclipse link 2.0.2.
Actually JPA does allow such interface relationships, but in this case you have to provide an entity class implementing the interface, in you case this will look as follows:
#OneToOne(targetEntity = AddressImpl.class)
private Adress adress;
JPA standard does not allow for interface fields (or Collection of interface fields) being entity relationships. Some JPA implementations do support it (e.g DataNucleus JPA), but its a vendor extension to the spec. Consequently you either use one of those implementations or change your model (or add extra annotations/XML to define what type is actually stored there).

Categories

Resources