Kevin Kesicki
Kevin Kesicki

Reputation: 165

Hibernate and JAXB annotations clashing

I am not particularly familiar with hibernate or JAXB so forgive me if I dont do a great job of explaining my problem.

The program I am working with already had set up a class of hibernate entities. My job was to add JAXB annotations to marshal the variables to an XML file.

@OneToMany(fetch = FetchType.LAZY, mappedBy = "Blah")
@XmlElementWrapper(name = "ListOfThings")
@XmlElement(name = "Thing")
private Set<Stuff> stuff = new HashSet<Stuff>(0);

When I try to run the program, I get this error:

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: package.class, no session or session was closed

I have 3 variables that I use the @XmlElement annotation with and two also have the @OneToMany annotation. Both @OneToMany variables give me that same error, but when I make them both transient, the program compiles perfectly well with the non-@OneToMany-annotated variable.

Any idea what my problem might be?

Upvotes: 0

Views: 969

Answers (2)

nico
nico

Reputation: 460

There are 2 solutions to that, which can be combined in order to have finest control of marshalling.

  • Provide an AccessorFactory which will create a custom Accessor. In this Accessor, you override

public abstract ValueT get(BeanT bean) throws AccessorException;

and if the pojo is not initialized, return null :

if (!Hibernate.isInitialized(valueT)) { return null; }

Note there is an annoying optimize method :

public Accessor<BeanT,ValueT> optimize(@Nullable JAXBContextImpl context) { return this; }

which can replace your custom Accessor, depending on the Accessor you override (see FieldReflection).

Do not forgot the following initialization of JAXBContext :

HashMap<String, Object> props = new HashMap<String, Object>(); props.put(JAXBRIContext.XMLACCESSORFACTORY_SUPPORT, true); JAXBContext jaxbContext = JAXBContext.newInstance(new Class[] { clazz }, props);

  • The 2nd solution is to override the AnnotationReader of JAXB, that way you can return the annotations you want, depending on the Class, Field, etc... So you can return XmlTransient annotation if you do not want your object to be marshalled. That is done that way :

HashMap<String, Object> props = new HashMap<String, Object>(); props.put(JAXBRIContext.ANNOTATION_READER, new CustomAnnotationReader()); JAXBContext jaxbContext = JAXBContext.newInstance(new Class[] { clazz }, props);

RuntimeInlineAnnotationReader is final and cannot be overriden... so you will have to copy the code.

I personnaly combined those two approches in order to modify marshalling depending on the context and content of the objects.

Upvotes: 0

Rob
Rob

Reputation: 6497

When JAXB writes the XML, it walks the whole data structure including all relationships. Hibernate is using lazy loading. You need to make sure that your entity is attached to an active hibernate session when you are having JAXB create the XML. This allows hibernate to do the required queries to fill in the lazy-loaded part of the data structure as JAXB asks for them.

Upvotes: 1

Related Questions