Friday, January 18, 2008

XML<->Java<->DB

JPA and JAXB both provide a way to annotate your java classes to tell how they should be serialized. JPA annotations defines how your class is mapped to the database, and JAXB annotations define how your class is serialized to XML. But is it possible to have both annotations on the same classe, to be able to convert your data from the database, to java object, then to XML or any combination of the 3?

Well yes it is, and it works pretty well :-)
Here is what your code will look like:

@XmlRootElement
@XmlType(propOrder={

"brand",
"personId"
})
@Entity
@Table(name = "car")

@NamedQueries({@NamedQuery(name = "Car.findByCarId", query = "SELECT c FROM Car c WHERE c.carId = :carId")})

public class Car implements Serializable {
private static final long serialVersionUID = 1L;

@XmlTransient
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)

@Column(name = "car_id", nullable = false)

private Integer carId;
@Column(name = "brand")

private String brand;
@JoinColumn(name = "person_id", referencedColumnName = "person_id")

@ManyToOne
private Person personId;

public Car() {

}

public Car(Integer carId) {
this.carId = carId;

}

public Integer getCarId() {
return carId;

}

public void setCarId(Integer carId) {

this.carId = carId;
}

public String getBrand() {

return brand;
}

public void setBrand(String brand) {

this.brand = brand;
}

@XmlElement(name = "owner")

public Person getPersonId() {
return personId;

}

public void setPersonId(Person personId) {

this.personId = personId;
}
}

There are several things to notice about that:

1)The JPA annotations decorate the attributes where the JAXB annotations decorate the bean properties (...the getters if you want...)

2)There is a catch when you have bidirectional relationships. XML doesn't allow bidirectional relationships (You cannot have element A in element B and element B in element A :-)). So if you convert from XML to Java, then try to persist the object in your DB, you will see that something goes wrong. So just make sure that you restore those missing relationships in your java object (eg, if person.car!=null, then car.owner shouldn't be null) and everything will work fine.

3 comments:

Michael said...

I have a schema and am generating java code from there and would like to persist it using JPA. Do you know any way of doing that?

christophe laudon said...

You could use Hyperjaxb3 (https://hyperjaxb3.dev.java.net). It does a pretty nice job of adding the right JPA annotations to your JAXB-generated classes.
Note that:
- There is no way to specify a many-to-many relationship yet.
- The default inheritance mechanism uses JOINED instead of TABLE_PER_CLASS.


I've circumvented both limitations using a drastic (and ugly) method that does string replaces in the entire set of JAXB/JPA classes using "replace" ANT tasks. If there's a better way to go about doing this, I'd be happy to know.

Steve said...

A bidirectional relationship can be easily restored by adding a method called afterUnmarshal to the child object. For example:

public void afterUnmarshal(Unmarshaller u, Object parent) {
this.parent = (Parent)parent;
}

JAXB automatically calls this after binding the child object.