I have an object graph that contains a cycle. How do I get JAXB to handle this? I tried using the #XmlTransient annotation in the child class but the JAXB marshaller still detects the cycle.
#Entity
#XmlRootElement
public class Contact {
#Id
private Long contactId;
#OneToMany(mappedBy = "contact")
private List<ContactAddress> addresses;
...
}
#Entity
#XmlRootElement
public class ContactAddress {
#Id
private Long contactAddressId;
#ManyToOne
#JoinColumn(name = "contact_id")
private Contact contact;
private String address;
...
}
This page in the "Unofficial JAXB Guide" offers three strategies for dealing with cycles. They are (in summary):
Mark one of the reference attributes that form the cycle as #XmlTransient.
Use #XmlID and #XmlIDREF so that the references are represented using XML ids arather than by containment.
Use the CycleRecoverable interface to deal with cycles programmatically.
The good thing about using JAXB is that it is a standard runtime with multiple implementations (just like JPA).
If you use EclipseLink JAXB (MOXy) then you have many extensions available to you for handling JPA entities including bi-directional relationships. This is done using the MOXy #XmlInverseReference annotation. It acts similar to #XmlTransient on the marshal and populates the target-to-source relationship on the unmarshal.
http://wiki.eclipse.org/EclipseLink/Examples/MOXy/JPA/Relationships
#Entity
#XmlRootElement
public class Contact {
#Id
private Long contactId;
#OneToMany(mappedBy = "contact")
private List<ContactAddress> addresses;
...
}
#Entity
#XmlRootElement
public class ContactAddress {
#Id
private Long contactAddressId;
#ManyToOne
#JoinColumn(name = "contact_id")
#XmlInverseReference(mappedBy="addresses")
private Contact contact;
private String address;
...
}
Other extensions are available including support for composite keys & embedded key classes.
To specify the EcliseLink MOXy JAXB implementation you need to include a jaxb.properties file in with your model classes (i.e. Contract) with the following entry:
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
XMLTransient almost always works for cycles. It might be a possibility that you have XMLTransient on the field level but you have not specified XMLAccessorType to be XmlAccessType.Field. If you don't specify anything the default is XmlAccessType.Property - or your getters. I have experienced Jaxb picking xml elements from getters from a class that I missed the accessor type annotations on and still work perfectly fine.
just look at this tutorial : Mapping cyclic references to XML by jaxb
I use it an it works well :)
We can use XStream library as well, I tried it one project where JAXB was giving cyclic error but XStream handled it successfully
Related
I have an entity as
#Getter
#Setter
#Entity
#Table(name = "feature")
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class Feature {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Column(name = "name")
private String name;
#Column(name = "description")
private String description;
#OneToMany(mappedBy = "featureId", fetch = FetchType.LAZY)
private transient Collection<FeatureComponent> components;
}
While in its Repository(Dao) file, I have
public interface FeatureDao extends JpaRepository<Feature, Integer> {
#Query("SELECT e FROM Feature e")
public List<Feature> getAll();
#Query("SELECT e FROM Feature e LEFT JOIN e.components fc WHERE e.id= :id")
public Feature getWithDetail(#Param("id") Integer id);
}
When I'm calling featureDao.getAll(); it returns all features but including components list filled and because of that, my response it being too large to load on client-side.
I'm unable to understand why it is happening when I'm using Lazy fetch mode and didn't mentioned joining with components in my getAll method.
Please help to resolve that issue,
Thanks in advance.
Just like #spOOm already mentioned I also suspect this is the side effect of Jackson Feature entity serialization into JSON triggering the load of all the components.
That is why using DTOs instead of plain Entities is usually advisable when returning data via a Controller. With DTOs, you clearly define whatever you want to include in the response to the caller. You can even reorganize your model so that it fits better the needs of the clients. You decouple your inner model and the model your API consumers know, making it possible to rework your inner model and still keep the same public model. You could have the following DTO.
public class FeatureSimpleDto {
private Integer id;
private String name;
private String description;
}
Then, in your Controller or Service (here you can find different opinions) you would basically convert your Feature entity into a FeatureSimpleDto that would be returned by your Controller. Here you can use mapping libraries such as MapStruct or you can do it on your own (I usually tend to prefer doing it on my own, one less dependency to rely on).
Using Lombok may be a problem, depending on the relationship between tables, try to create getters manually in entity classes.
Thanks to everyone for providing workarounds... But every work item requires lots of changes which were not possible for me...
Luckily I found a solution that is working fine for me... Better to post here...
Step-1: Add dependency in pom.xml file
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-hibernate5</artifactId>
</dependency>
Step-2: Add a 'Bean' for Hibernate Module
We can add bean in any file having #Configuration annotation... Even, we can add in Springboot main application file(where we have main method).
#Bean
public Module datatypeHibernateModule() {
return new Hibernate5Module();
}
That's it, Happy Coding...
I have Spring Boot Application with Web, JPA, H2, Web, Lombok dependency.
I have entities as follows
#NoArgsConstructor
#AllArgsConstructor
#Data
#Entity
#Getter
public class Book {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(nullable = false)
private String name;
#Column(nullable = false)
private Integer pageCount;
#ManyToOne
#JoinColumn(name = "author_id")
private Author author;
}
#NoArgsConstructor
#AllArgsConstructor
#Data
#Entity
#Getter
public class Author {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(nullable = false)
private String firstName;
#Column
private String lastName;
}
I am getting following JSON in repose for the /books REST endpoint
[
{},
{}
]
If I add getters and setters to entity it works fine. How can I get the actual values in response without adding getters setters as I am using #Data annotation
First of all , In spring it is not a good approach to return the entity object directly from a controller to be converted to json. Use the DTO approach so that you will always have a DTO object returned from the controller endpoint. This way you will have more control over the type and structure of data you want to retun from the endpoint.
Read here about the advantages of using a DTO .
Secondly, verify that your tables follow the basic spring jpa naming conventions or is same as the entity class names if not please add the #Table(name="") annotation to specify the table name. Check that data is being populated to your entity classes.
Remove the #Data annotation.
#Data should not be used on JPA Entities as Entity toString, equals, and hashCode methods need to be authored in a very specific manner. See here for details.
After searching a lot and looking on the answers to my question found a solution.
If I run my spring boot app using java -jar from cmd it works fine. But not with IDE so issue was with IDE configuration.
I am using Eclipse IDE
Right Click on your project -> Properties
Then from Properties window
Java Compiler -> Annotation Processing -> Enable Annotation Processing
This option should be checked then annotations gets processed. I followed https://www.baeldung.com/lombok-ide from Tinku's answer on this question.
need to add Lombok plugin in maven or gradle file link How to configure Lombok with maven-compiler-plugin?.
If you are using IDE then need to install Lombok plugin link https://www.baeldung.com/lombok-ide
If you are using Eclipse check to have Lombok installed:
I have a party class that's mapped to a table and is annotated with #entity like so:
#entity
#Table(name = "PARTY")
public class party{
#Id
protected long partyId;
}
I also have a class representing an address:
#entity
#Table(name = "ADDRESS")
public class address{
#Id
protected long partyId;
}
The party Id can be used as a foreign key to get an address from the address table.
Sometimes I want to return a party by itself and sometimes I want to return a party with an address. I thought I could just make a separate class that extends party but adds in the address like so:
public class partyWithAddress extends party{
#OneToMany
private List<Address> addresses;
}
But I get an error:
Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Not a managed type: partyWithAddress
I read about entity inheritance but I still am pretty lost. Can anyone help?
If your class is not entity then it wont be recognized by JPA, you need to annotate it with #Entity first , then comes which inheritance policy you would like to use , by default the singltable inheritance is used which means all children and their parent is in one table and this is recommended in case you do not have much difference between your entities, for further information about inheritance check this link
I'd highly suggest avoiding inheritance hereand just add
#OneToMany
List<Address> addresses;
to the Party entity, and handling in your code the case for that list of Addresses being empty/null.
We're using entity classes for two purposes:
As a database model, i.e. Hibernate #Entity
As a data model sent over to the front end as JSON
Imagine an #Entity has a number of bulky collections, e.g.:
#Entity
#Table(name = "customer")
public class Customer {
#Id
private Integer id;
#Column(name = "name")
private String name;
#OneToMany(mappedBy = "customer", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private List<Order> orders;
#OneToMany(mappedBy = "customer", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private List<Counterparty> counterparties;
/* ... a lot of other properties, including collections ... */
}
Now, we work with Customer in two major ways:
Fetch an individual customer for viewing/editing, complete with all collections.
Fetch a list of customers, with just their "core" properties required for display; we don't need any of its child collections loaded as we're not using them and they generate a lot of additional queries.
My preferred solution would be explicit initialization of collections for the case 1, and keeping them null or empty for the case 2.
The problem is that when Jackson serializes an object into JSON, it goes over all properties, including collection ones, so they are forced to be initialized. Adding #JsonIgnore is not an option (we need those collections for case 1), adding #Transient to keep Hibernate away from them is not an option either (we have to be able to store collections after editing).
Another alternative would be, of course, creating a different model of Customer without collections and using that for scenario 2, but that means maintaining two varieties of the same entity and I'd like to avoid that.
How can I disable Hibernate's implicitly loading of these collections, so that they are only initialized explicitly (via e.g. Hibernate.initialize(customer.orders)), while retaining the possibility of persisting them when needed?
There is no way to do it in Hibernate.
Alternatives:
Use DTOs. This has the advantage of tailoring the objects to be serialized to the exact needs of the client consuming the resulting JSON. Also, the domain model (Hibernate entities) is decoupled from the serialization logic, allowing the two to evolve independently.
Use custom serializers.
Eventually I've solved it in the "DTO-fashion" but with minimal overhead, by adding an alternative getter for each property, like so:
#Entity
#Table(name = "customer")
public class Customer {
...
#OneToMany(mappedBy = "customer", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JsonIgnore
private List<Order> orders;
/* "Normal" getter to be used in the back-end */
#JsonIgnore // <-- Important as it hides it from Jackson
public List<Order> getOrders() {
return orders;
}
/* Non-initializing getter for serialization for the front-end */
public List<Order> getOrdersNonInit() {
return Hibernate.isInitialized(orders) ? orders : null;
}
...
}
The following is just one possible solution and depends on your environment.
There's a project called "jackson-datatype-hibernate" from the FasterXML project. You can find it at https://github.com/FasterXML/jackson-datatype-hibernate which allows to define a custom serializer with includes disabling of lazy fields. (I think this default for this serializer).
Code could look like the following:
ObjectMapper mapper = new ObjectMapper();
Hibernate4Module hibernateModule = new Hibernate4Module();
mapper.registerModule(hibernateModule);
In my use-case, I would like to #Embedded a class C in an entity.
Another entity refers to C with #OneToMany association and therefore C is annotated with #Entity.
I am aware that this seems like bad design, yet I believe that it makes perfect sense in my case.
Is it possible to force Hibernate to embed an Entity? If I try it, Hibernate complains about a missing setter for the id property of C.
I think the problem comes from this:
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
private Long id;
Why not just create the entity that you want, and in that entity, embed C as well. That way you have C in both classes, one as embedded and another as embedded of the new entity.
#Embeddable
public class Contact {
private String firstname;
private String lastname;
// getters and setters removed.
}
and here is your embedding class:
#Entity
public class Student {
#Embedded
private Contact contact;
}
and here is the new entity that embeds contact also
#Entity
public class FirmContact {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private int contactId;
#Embedded
private Contact contact;
}
And finally the class that insists the contact must be an entity:
#Entity
public class Business {
#OneToOne(cascade=CascadeType.ALL)
private FirmContact contacts;
}
It'll just be a couple of extra steps in java to populate the object, but it should do the mapping you want. I hope this helps.
Hibernate doesn't allow you to treat an Embeddable as an Entity or to embed an Entity. According to Hibernate types:
an Embeddable, doesn't have an identifier, since it's state is part of an owning Entity.
an Entity cannot be embedded, because each Entity has a distinct life-cycle.
Since another class already has a #OneToMany association to class C, it's obvious you cannot turn it into an Embeddable.
More, a bidirectional #OneToMany association will perform better than an embeddable collection.
What you can do, is to use it as a #OneToOne association in the entity where you wanted to embed the C entity. You can make that target entity be the owning side of the association so that the C association is bound to the target entity life-cycle.