I am trying to use the CycleRecoverable interface to manage cyclic issues in my object model for bi-directional relationships. Guides such as this and this, tell you to use CycleRecoverable, but I don't have it on my class path. I'm confused as to what I actually need, and an explanation as to why I need it. I am not using Maven, so I can't follow the second links advice, and even if I was using Maven, I don't understand why I need more packages in order to use JAXB which I thought was included in SE6. What jar files do I actually need to include to use this interface and why? The only CycleRecoverable on my classpath is com.sun.xml.internal.bind.CycleRecoverable
The (un)official JAXB guide notates the interface, but makes no mention of how to actually use it
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB 2 (JSR-222) expert group.
As an alternative to the CycleRecoverable mechanism in the JAXB reference implementation you may be interested in the #XmlInverseReference extension in MOXy:
Customer
import javax.persistence.*;
#Entity
public class Customer {
#Id
private long id;
#OneToOne(mappedBy="customer", cascade={CascadeType.ALL})
private Address address;
}
Address
The #XmlInverseReference annotation specifies the field name that maps the other direction of this relationship. This is similar to how bidirectional relationships are mapped in JPA.
import javax.persistence.*;
import org.eclipse.persistence.oxm.annotations.*;
#Entity
public class Address implements Serializable {
#Id
private long id;
#OneToOne
#JoinColumn(name="ID")
#MapsId
#XmlInverseReference(mappedBy="address")
private Customer customer;
}
For More Information
http://blog.bdoughan.com/2010/07/jpa-entities-to-xml-bidirectional.html
http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html
Related
I have a set of Java classes with the following UML diagram:
public class Invoice {
#Id
private long id;
...
}
public class InvoiceDetail {
#Id
private long id;
...
private String productName;
private int quantity;
private double price;
}
My purpose is using JPA annotations to establish the different relationships between them. There is a composition relationship between Invoice and InvoiceDetail, which is resolved using #Embedded and #Embeddable annotations for Invoice and InvoiceDetail respectively. However, a problem appears by establishing the relationships between InvoiceDetail, Class3 and Class4. In these relationships InvoiceDetail must be annotated as #Entity. However, when a class is annotated at the same time as #Entity and #Embeddable, the corresponding server will throw a runtime error during the deployment.
Basing on the information of this website, I have written the following possible solution:
#Entity
public class Invoice {
#Id
private long id;
...
#ElementCollection
#CollectionTable(name="INVOICEDETAIL", joinColumns=#JoinColumn(name="INVOICE_ID"))
private List<InvoiceDetail> invoiceDetails;
...
}
Would be this right in order to resolve my problem?
Thanks in advance.
Although without knowing what the classes really are it is hard to tell, I suppose that you have a design problem. The composition between Class1 and Class2 says that any Class2 instance only exists within the lifecycle of a corresponding Class1 instance. But on the other hand you have Class3 instances and Class4 instances which can / must have a relationship to a Class2 instance.
What I'm trying to say is that from my point of view the relationship between Class1 and Class2 should be a simple association and not a composition. Following this path Class2 would be an Entity in JPA and then you should have your problem solved.
I usually use #Embeddable for classes whose instances never exist by themselfes and #Entity for any class whose instances can exist without other instances. An address for example could be implemented either way but not on the same system. Address would be #Embeddable if I don't want to link addresses but it had to be #Entity if I want to make sure the same address isn't saved in more than one row.
[edit: added after classes 1 and 2 were renamed to Invoice and InvoiceDetails]
Having a composition between Invoice and InvoiceDetails makes perfect sense. But I still think you should avoid the need of double personality for InvoiceDetails. I can think of two solutions (both refactorings):
If you prefer having InvoiceDetails as #Embeddable you could change the associations of Class3 and Class4 to Invoice instead of InvoiceDetails. InvoiceDetails would still be traversable via the Invoice object.
If you prefer keeping the associations as is you could declare InvoiceDetails to be an entity. You could still achieve your composition with a cascading delete (see javax.persistence.CascadeType). As it seems that InvoiceDetails already has it's own table, this probably is the better option.
I checked my JPA applications and haven't found any occurence of the same class being #Entity and #Embeddable. Honestly, I doubt if this is possible at all because the official javadoc of #Embeddable says:
Specifies a class whose instances are stored as an intrinsic part of an owning entity and share the identity of the entity.
As #Entity has it's own identity, you would try to declare the same object having two identities - and this can't work.
[/edit]
[edit2: adding code for solution proposal #2]
This code should work with some assumptions (see below). This is the implementation of bi-directional navigation for a 1:n-relationship.
#Entity
public class Invoice {
#Id
private long id;
#OneToMany(mappedBy="invoice", cascade = CascadeType.ALL)
private List<InvoiceDetail> details;
}
#Entity
public class InvoiceDetails {
#Id
private long id;
#ManyToOne
#JoinColumn(name="invoice_id")
private Invoice invoice;
}
Assumptions: Tables are named like the entities, the foreign key column for invoice_details table is named "invoice_id" and both tables have a primary key column named "id". Note that the mappedBy-value "invoice" refers to the entity field while the name-value "invoice_id" refers to the database table.
Be cautious when deleting an Invoice object whose InvoiceDetails still are referenced by your Class3 or Class4 instances - you have to release these references first.
For information about JPA refer to these resources:
The Java EE 7 Tutorial: Persistence
Wikibooks: Java Persistence
Javadoc of Package javax.persistence
[/edit]
when I use the JPA #Version annotaton in an #Embeddable I get the following exception pointing at my Updateable class:
org.hibernate.AnnotationException: Unable to define #Version on an embedded class
Here is my code:
#Embeddable
public class Updateable {
#Version
private long modcount;
private String updatedBy;
private DateTime updatedAt;
// getters & setters
}
#Entity
public class SomeEntity {
#Id
private Long id;
#Embedded
private Updateable updateAudit;
// other stuff
}
Is it not possible to have a #Version in an #Embeddable, or is this Hibernate specific?
An embeddable class is just a convinience way of declaring reusable entity elements, i.e. your Updateable could be used in other entities without having to add the fields and the mapping again.
As such, embeddables are part of the entity (as the name suggests they are embedded) and thus independent versioning doesn't make sense.
Adding the #Version annotation to the embeddable only would also not make much sense since the embeddable itself can't be versioned and you'd have to deal with cases where multiple embeddables are contained in a single entity (e.g. which version should be used in that case?). So since #Version only makes sense for entities it's easier to just allow that annotation for entities or mapped superclasses.
Actually although the JPA spec recommends that version properties are numeric, strings or timestamps Hibernate seems to provide user defined version types:
The version column may be a numeric (the recommended solution) or a timestamp. Hibernate supports any kind of type provided that you define and implement the appropriate UserVersionType.
So what you might be able to do (not tested, just derived from the docs) if you want to use Updateable as your version is to provide a custom user type for Updateable and then use it like this:
#Entity
public class SomeEntity {
#Id
private Long id;
#Type( "your.custom.UserVersionType" )
#Version
private Updateable updateAudit;
// other stuff
}
I have a JPA #Entity class Place, with some properties holding some information about a place, such as name of place, description, and URLs of some images.
For the URLs of images, I declare a List<Link> in my entity.
However, I am getting this error:
Basic attribute type should not be a container.
I tried to remove #Basic, but the error message is still there. Why does it shows this error?
You can also use #ElementCollection:
#ElementCollection
private List<String> tags;
You are most likely missing an association mapping (like #OneToMany) and/or #Entity annotation(s).
I had a same problem in:
#Entity
public class SomeFee {
#Id
private Long id;
private List<AdditionalFee> additionalFees;
//other fields, getters, setters..
}
class AdditionalFee {
#Id
private int id;
//other fields, getters, setters..
}
and additionalFees was the field causing the problem.
What I was missing and what helped me are the following:
#Entity annotation on the generic type argument (AdditionalFee) class;
#OneToMany (or any other type of association that fits particular business case) annotation on the private List<AdditionalFee> additionalFees; field.
So, the working version looked like this:
#Entity
public class SomeFee {
#Id
private Long id;
#OneToMany
private List<AdditionalFee> additionalFees;
//other fields, getters, setters..
}
#Entity
class AdditionalFee {
#Id
private int id;
//other fields, getters, setters..
}
Change #basic to #OneToMany for List types
Or you can mark it as #Transient if it doesn't exist on DB table.
#Transient
private List<String> authorities = new ArrayList<>();
As the message says, #Basic should not be used for containers (e.g. Java collections). It is only to be used for a limited list of basic types. Remove the #Basic annotation on that field.
If, as you say in the question, the error message is still there, you might need to try the following steps in order:
Save the file
Close and reopen the file
Clean and rebuild the project
Restart the IDE
(these are generic steps, which I use when an IDE is generating a compilation error that obviously makes no sense.)
This can also happen when your class is missing its #Entity annotation. When you get weird warnings like these, sometimes it helps to try and compile and see if the compiler complains.
The error seems not have impact on GAE since I can run the app and store data into storage. I guess it's a bug in IntelliJ IDEA and you can simply ignore it.
'Basic' attribute type should not be a container
This error occurs when you declare an existing entity as an attribute in the current Entity without declaring the relationship type which could be either of the JPA relationships.
Detailed Article on JPA relationships
I have a class with it's own #Embeddable id class, let's call it ICompanyId. There are 2+ classes that implement ICompanyId. Classes that implement ICompanyId are the embedded id's for classes that implement ICompany (which there are 2+ implementing classes).
So, what I would like to do is this:
#EmbeddedId
public ICompanyId getId() { /* blah blah */ }
How do I tell Hibernate which implementing class to use? With a #ManyToOne or #OneToMany, all I would have to do is to specify a value in the targetEntity, but #EmbeddedId offers no such options. Can anyone help?
Jason
Have you tried #Target(CompanyIdImpl.class) (see: #Target)?
Keep in mind that this is a Hibernate-specific solution.
I've implemented a REST API based on JPA and JAXB.
I have a classes roughly like this (very simplified):
#Entity
#XmlRootElement
...
public class Thing {
#Id
#GeneratedValue
...
#XmlAttribute
#XmlID
#XmlJavaTypeAdapter(JAXBLongAdapter.class)
private Long id;
...
}
Hibernate (my current JPA provider) generates numbers as the id value, but they are naturally unique only for one one type, Thing in this example.
Now XSD says that xsd:id (#XmlID) is a NCString which cannot be a plain number so i prepended a '_' to numbers in the JAXBLongAdapter. - like '_1'
Now the schema validator complains:
[org.xml.sax.SAXParseException: cvc-id.2: There are multiple occurrences of ID value '_1'.]
If I understand this correctly a xsd:ID element must have a (string) value that is globally unique in the xml document. But this is very opposite of the common way of using IDs in databases.
What do I do now?
I thought of three things:
Create a JAXBLongAdapter for each type with a type specific prefix?
Using another JPA id generator, perhaps UUID? - But which one?
Stop using #XmlID and #XmlIDREF, which creates redundancy and general messiness.
It seems that I now have to change the Database schema to use different IDs.
- But it would be nice if the IDs stayed short, because they appear in URLs.
My question: Is there a ID generator that is comparably fast and is globally unique?
Or is there another way of tackling this?
EDIT:
This hack kinda works, leaving the JPA IDs intact.
#XmlID
#XmlAttribute(name="id")
private String getXmlID(){
return String.format("%s-%s", this.getClass().getSimpleName(), this.getId().toString());
}
private void setXmlID(String xmlid){
String prefix = String.format("%s-", this.getClass().getSimpleName());
if(xmlid.startsWith(prefix)){
this.id = Long.parseLong(xmlid.substring(prefix.length()));
}else{
throw new IllegalArgumentException(xmlid+" does not look like "+prefix+"###");
}
}
By moving the JAXB Annotation from the field to dedicated private getters/setters for the XmlID.
That's exactly what I had done with for some time.
You can ask yourself what is actually #XmlID for this domain object when marshalled?
I once thought #XmlID and #XmlIDREF can solve the circular problem in JAXB.
Here comes what I'm doing with my JPA entities along with JAXB annotations.
Do not give the simple JPA #Id up. That's the heart of JPA.
#XmlRootElement
public class Parent {
#Id
#XmlAttribute
private Long id;
#OneToMany
#XmlElement(name = "child")
#XmlElementWrapper
private Collection<Child> children;
}
#XmlRootElement
public class Child {
#XmlAttribute
private Long getParentId() {
return parent.getId();
}
#Id
#XmlAttribute
private Long id;
#ManyToOne
#XmlTransient // for preventing infinite circular problem
private Parent parent;
}
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
WHAT THE ANSWER SHOULD BE
The answer should be to use the #XmlSchemaType annotation on both the #XmlID and #XmlIDREF properties. Unfortunately the JAXB RI does not leverage that combination, and EclipseLink MOXy only leverages it for #XmlID. I have entered the following MOXy bug, which we could fix if you are interested in this approach:
http://bugs.eclipse.org/386569
Employee
package forum11791735;
import java.util.List;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Employee {
#XmlID
#XmlJavaTypeAdapter(JAXBLongAdapter.class)
#XmlSchemaType(name="long")
private Long id;
#XmlIDREF
#XmlSchemaType(name="long")
private Employee manager;
#XmlElement(name="report")
private List<Employee> reports;
}
WORK AROUND
The error you are seeing is appears to be due to schema validation. Is it possible for you to disable schema validation or set a ValidationEventHandler on the Unmarshaller to ignore these errors?
http://blog.bdoughan.com/2010/12/jaxb-and-marshalunmarshal-schema.html
ALTERNATIVES
If you are using #XmlID/#XmlIDREF to map bidirectional relationships then you may be interested in MOXy's #XmlInverseReference extension:
http://blog.bdoughan.com/2010/07/jpa-entities-to-xml-bidirectional.html