Play Framework 2.1 ebean construction - java

I just started using Play Framework 2.1.1 as a Java developer.
Got some eBeans from an existing database and I'm building a RESTful architecture to serve and save those beans from a RESTful client web application.
I have the following beans definitions. Feed is the core object of the application. For simplicity I have not included getters and setters and many other beans, I'm focusing on the ones that give me troubles:
#Entity
public class Feed extends Model implements Serializable
{
#Id
Long feedId;
...
...
...
#JsonManagedReference("feed-item")
#OneToMany(fetch = FetchType.LAZY, mappedBy = "feed")
List<Item> items;
#JsonManagedReference("feed-userFeed")
#OneToMany(fetch = FetchType.LAZY, mappedBy = "feed")
List<UserFeed> userFeeds;
}
#Entity
public class Item extends Model implements Serializable
{
#Id
Long itemId;
...
...
...
Long urlId;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "url_id")
Url url;
#Formats.DateTime(pattern = "yyyy-MM-ddThh:mm:ss")
Timestamp itemPublishedAt;
#Formats.DateTime(pattern = "yyyy-MM-ddThh:mm:ss")
Timestamp itemUpdatedAt;
#Formats.DateTime(pattern = "yyyy-MM-ddThh:mm:ss")
Timestamp createdAt;
#Version
Timestamp updatedAt;
Long feedId;
#ManyToOne(fetch = FetchType.LAZY)
#JsonBackReference("item-feed")
#JoinColumn(name = "feed_id")
Feed feed;
Long urlId;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "url_id")
Url url;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "item")
#JsonManagedReference("item-unseen")
List<Unseen> unseen;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "item")
#JsonManagedReference("item-score")
List<Score> scores;
}
#Entity
public class User extends Model implements Serializable
{
#Id
Long userId;
...
...
...
#OneToMany
#JsonManagedReference("user-userFeed")
List<UserFeed> userFeeds;
#OneToMany
#JsonManagedReference("user-userTag")
List<UserTag> userTags;
#OneToMany
#JsonManagedReference("user-unseen")
List<Unseen> unseen;
#OneToMany
#JsonManagedReference("user-score")
List<Score> scores;
}
#Entity
public class Score extends Model implements Serializable
{
#Id
Long scoreId;
...
...
...
Long itemId;
#ManyToOne(fetch=FetchType.LAZY)
#JsonBackReference("score-item")
#JoinColumn(name = "item_id")
Item item;
Long userId;
#ManyToOne(fetch=FetchType.LAZY)
#JsonBackReference("score-user")
#JoinColumn(name = "user_id")
User user;
}
Here's my feed controller:
import me.zenfeed.model.Feed;
import org.codehaus.jackson.JsonNode;
import play.db.ebean.Model;
import play.libs.Json;
import play.mvc.BodyParser;
import play.mvc.Controller;
import play.mvc.Result;
public class FeedController extends Controller
{
public static Result delete(Long id)
{
new Model.Finder<>(Long.class, Feed.class).byId(id).delete();
return ok();
}
/* return a JSON with all of the objects found*/
public static Result get()
{
return ok(Json.toJson(new Model.Finder<>(Long.class, Feed.class).fetch("userFeeds", new FetchConfig().query()).where().eq("userFeeds.userId", Long.parseLong(session("connectedUserId"))).findList()));
}
/* accept a JSON with the object to save and return a JSON with the new object*/
#BodyParser.Of(BodyParser.Json.class)
public static Result post()
{
JsonNode json = request().body().asJson();
Feed f = Json.fromJson(json, Feed.class);
f.save();
return ok(Json.toJson(f));
}
/* accept a JSON with the object to save and return a JSON with the new object*/
#BodyParser.Of(BodyParser.Json.class)
public static Result put(Long id)
{
JsonNode json = request().body().asJson();
Feed f = Json.fromJson(json, Feed.class);
f.update(id);
return ok(Json.toJson(f));
}
}
Here's a custom function I use to make a query on the DB with some parameters:
List<Item> l = new Model.Finder<>(Long.class, Item.class)
.select("itemId, feedId, urlId, title, description, content, author, categories, itemPublishedAt, itemUpdatedAt, wordCount, fresh, vector, createdAt, updatedAt")
.fetch("unseen", "itemId", new FetchConfig().query())
.fetch("scores", "score", new FetchConfig().query())
.fetch("feed.userFeeds", "userId", new FetchConfig().query())
.where()
.eq("feed.userFeeds.userId", Long.parseLong(session("connectedUserId")))
.eq("feed.feedId", feedId)
.orderBy("scores.score")
.findList();
I have several problems I would like help with:
- When I try a POST request that calls the method post() of this controller I get the following exception:
[RuntimeException: org.codehaus.jackson.map.JsonMappingException: Can not handle managed/back reference 'user-score': no back reference property found from type [collection type; class java.util.List, contains [simple type, class me.zenfeed.model.Score]]]
I think I put the back reference in the Score model correctly, but I don't understand... Are my ebeans correct?
Is there something wrong in my controller? Can something be made better?
I now understand I saw a wrong tutorial. The argument for #JsonManagedReference and #JsonBackReference must be the same. So for feed-item relationship the correct annotations are #JsonManagedReference("feed-item") and #JsonBackReference("feed-item").
As you can see I tried to format the Timestamp objects with an HTML5 like format, but with no luck. I always get the Long number. Should I make a conversion to a Date object? If yes where?
- When I run the query with my custom function I get the URL object populated, even though I clearly excluded it from my select statement (I only would like the URL_ID, not the entire object).
I went manual way with the generation of an HashMap for the properties I care of and gave that to the Json.toJson() method.
Thank you for your help.

Related

Problem with saving foreign key with #OneToOne annotation. Saving as null

I have two entities (Project, OtherData) with one abstract entity. I'm using MySQL and Quarkus framework.
Problem: When I try to save Project entity field project_id remains null.
Table schemas:
On next picture there is shown, fk constraint in "project_other_data" table:
Abstract Entity:
#MappedSuperclass
public class AbstractEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
protected Long id;
// getters and setters
}
Project Entity
#Entity
#Table(name = "projects")
public class Project extends AbstractEntity {
#NotNull
#Column(name = "name")
private String name;
#NotNull
#Column(name = "surname")
private String surname;
#Column(name = "date_create")
#JsonbDateFormat(value = "yyyy-MM-dd")
private LocalDate dateCreate;
#Column(name = "date_update")
#JsonbDateFormat(value = "yyyy-MM-dd")
private LocalDate dateUpdate;
#OneToOne(mappedBy = "project", cascade = CascadeType.ALL)
private OtherData otherData;
// getters and setters
}
OtherData Entity
#Entity
#Table(name = "project_other_data")
public class OtherData extends AbstractEntity {
#OneToOne
#JoinColumn(name = "project_id")
private Project project;
#Column(name = "days_in_year")
private Integer daysInYear;
#Column(name = "holidays_in_year")
private Integer holidaysInYear;
#Column(name = "weeks_in_year")
private Integer weeksInYear;
#Column(name = "free_saturdays")
private Integer freeSaturdays;
#Column(name = "downtime_coefficient")
private BigDecimal downtimeCoefficient;
#Column(name = "changes")
private Integer changes;
// getters and setters
}
Saving entities with code:
#Path("projects")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public class ProjectRest {
#Inject
ProjectService projectService;
#POST
public Response saveProject(Project project) {
return Response.ok(projectService.saveProject(project)).build();
}
}
#RequestScoped
#Transactional
public class ProjectService {
#Inject
EntityManager entityManager;
public Project saveProject(Project project) {
if (project.getId() == null) {
entityManager.persist(project);
} else {
entityManager.merge(project);
}
return project;
}
}
I was able to reproduce the problem by POSTing a new Project with an embedded OtherData. The body I used for the POST:
{
"name": "John",
"surname": "Doe",
"otherData": {}
}
Point is: the database entity is also used as DTO. Thus, the field project in otherData for the request body is set to null (since no Project is passed along this would be a recursive infinite definition).
During processing the entity from the rest controller to the service to the repository, the project of otherData is never set. A quick fix is to modify ProjectService::saveProject as follows:
public Project saveProject(Project project) {
project.getOtherData().setProject(project); // This line was added
if (project.getId() == null) {
entityManager.persist(project);
} else {
entityManager.merge(project);
}
return project;
}
This will fix the database issue (the project_id will be set), but leads to the next issue. The response body cannot be serialized due to an
org.jboss.resteasy.spi.UnhandledException: javax.ws.rs.ProcessingException: RESTEASY008205: JSON Binding serialization error javax.json.bind.JsonbException: Unable to serialize property 'otherData' from com.nikitap.org_prod.entities.Project
...
Caused by: javax.json.bind.JsonbException: Recursive reference has been found in class class com.nikitap.org_prod.entities.Project.
The object structure is cyclic (project references otherData, which return references project, ...) and Jackson is unable to resolve this cycle.
To fix this issue, I would suggest to separate DTOs and database entity and explicitly map between them. In essence:
Structure the Dto-object to represent the JSON-Request and -Response you expect to receive, in a non-cyclic order
Transfer JSON-related annotations from the database entity classes to the DTO classes
In the service- or repository-layer (your choice), map the DTO to the database entites, setting all fields (including the references from project to otherData and vice-versa)
In the same layer, map database-entites back to non-cyclic DTOs
Return the DTOs from the REST endpoint

Localization of entity fields in Spring Hibernate

I have two entities with fields that I´d like to localize. However, I´m not sure how to implement that correctly, because I would need to have a reference to the entities as well as a reference to the field that is translated, in order to have a shared "i18n" table.
#Entity
public class EntityA {
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<Translation> name;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<Translation> description;
}
Second entity
#Entity
public class EntityB {
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<Translation> name;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<Translation> shortDescription;
}
Translation Entity
#Entity
#Table(name = "i18n")
public class Translation {
private String languageCode;
private String translation;
//private String referenceToEntity
//private String referenceToField
}
Is there a given way to enable internationalization on entity fields in Spring or at least some kind of workaround to make it working without too much overhead?
EDIT
I´ve written a short post about how I solved it using XmlAnyAttribute https://overflowed.dev/blog/dynamical-xml-attributes-with-jaxb/
I did some research and found this #Convert JPA annotation. You would need to encapsulate the name and description properties into an object (that implements AttributeConverter), and use a convertion class to specify how it will be translated when persisted, and how will it be translated when retreived.
To execute translations on persistence and retrieval, you can consume Google translate's API.
Here:
#Entity
public class EntityA {
#Convert(converter = DescriptionConverter.class)
private Description description
// getters and setters
},
The encapsulated object, something like:
public class Description {
private String name;
private String language;
private String description;
// Getters and Setters.
}
And the translation applies here:
#Converter
public class DescriptionConverter implements AttributeConverter<Description, String> {
#Override
public String convertToDatabaseColumn(Description description) {
// consume Google API to persist.
}
#Override
public Document convertToEntityAttribute(String description) {
// consume Google API to retrieve.
}
}
this tutorial helped me a lot. i hope it will help you too. i used the second way and it's work perfectly.Localized Data – How to Map It With Hibernate

Hibernate 4.3 Cascade Merge Through Multiple Lists With Embeded ID

Hibernate 4.3.11
I have an issue saving the following object graph in hibernate. The Employer is being saved using the merge() method.
Employer
|_ List<EmployerProducts> employerProductsList;
|_ List<EmployerProductsPlan> employerProductsPlan;
The Employer & EmployerProducts have a auto generated pk. The EmployerProductsPlan is a composite key consisting of the EmployerProducts id and a String with the plan code.
The error occurs when there is a transient object in the EmployerProducts list that cascades to List<EmployerProductsPlan>. The 1st error that I encountered which I have been trying to get past was an internal hibernate NPE. This post here perfectly describes the issue that I am having which causes the null pointer Hibernate NullPointer on INSERTED id when persisting three levels using #Embeddable and cascade
The OP left a comment specifying what they did to resolve, but I end up with a different error when changing to the suggested mapping. After changing the mapping, I am now getting
org.hibernate.NonUniqueObjectException: A different object with the same identifier value was already associated with the session : [com.webexchange.model.EmployerProductsPlan#com.webexchange.model.EmployerProductsPlanId#c733f9bd]
Due to other library dependencies, I cannot upgrade above 4.3.x at this time. This project is using spring-boot-starter-data-jpa 1.3.3. No other work is being performed on the session other than calling merge() and passing the employer object.
Below is the mappings for each class:
Employer
#Entity
#Table(name = "employer")
#lombok.Getter
#lombok.Setter
#lombok.EqualsAndHashCode(of = {"employerNo"})
public class Employer implements java.io.Serializable {
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "EMPLOYER_NO", unique = true, nullable = false)
private Long employerNo;
.....
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "employer", orphanRemoval = true)
private List<EmployerProducts> employerProductsList = new ArrayList<>(0);
}
EmployerProducts
#Entity
#Table(name = "employer_products")
#Accessors(chain = true) // has to come before #Getter and #Setter
#lombok.Getter
#lombok.Setter
#lombok.EqualsAndHashCode(of = {"employerProductsNo"})
public class EmployerProducts implements Serializable {
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "employer_products_no", unique = true, nullable = false)
private Long employerProductsNo;
#ManyToOne
#JoinColumn(name = "employer_no", nullable = false)
private Employer employer;
......
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "employerProducts", orphanRemoval = true)
private List<EmployerProductsPlan> employerProductsPlanList = new ArrayList<>(0);
}
EmployerProductsPlan
#Accessors(chain = true) // has to come before #Getter and #Setter
#lombok.Getter
#lombok.Setter
#lombok.EqualsAndHashCode(of = {"id"})
#Entity
#Table(name="employer_products_plan")
public class EmployerProductsPlan implements Serializable {
#EmbeddedId
#AttributeOverrides({ #AttributeOverride(name = "plan", column = #Column(name = "epp_plan", nullable = false)),
#AttributeOverride(name = "employerProductsNo", column = #Column(name = "employer_products_no", nullable = false)) })
private EmployerProductsPlanId id;
#ManyToOne
#JoinColumn(name = "employer_products_no")
#MapsId("employerProductsNo")
private EmployerProducts employerProducts;
}
I am populating the employerProducts above with the same instance of the EmployerProducts object that is being saved. It is transient and has no id populated as it does not existing in the db yet.
EmployerProductsPlanId
#Accessors(chain = true) // has to come before #Getter and #Setter
#lombok.Getter
#lombok.Setter
#lombok.EqualsAndHashCode(of = {"plan", "employerProductsNo"})
#Embeddable
public class EmployerProductsPlanId implements Serializable {
private String plan;
private Long employerProductsNo;
// This was my previous mapping that was causing the internal NPE in hibernate
/* #ManyToOne
#JoinColumn(name = "employer_products_no")
private EmployerProducts employerProducts;*/
}
UPDATE:
Showing struts controller and dao. The Employer object is never loaded from the db prior to the save. Struts is creating this entire object graph from the Http request parameters.
Struts 2.5 controller
#lombok.Getter
#lombok.Setter
public class EditEmployers extends ActionHelper implements Preparable {
#Autowired
#lombok.Getter(AccessLevel.NONE)
#lombok.Setter(AccessLevel.NONE)
private IEmployerDao employerDao;
private Employer entity;
....
public String save() {
beforeSave();
boolean newRecord = getEntity().getEmployerNo() == null || getEntity().getEmployerNo() == 0;
Employer savedEmployer = newRecord ?
employerDao.create(getEntity()) :
employerDao.update(getEntity());
setEntity(savedEmployer);
return "success";
}
private void beforeSave() {
Employer emp = getEntity();
// associate this employer record with any products attached
for (EmployerProducts employerProduct : emp.getEmployerProductsList()) {
employerProduct.setEmployer(emp);
employerProduct.getEmployerProductsPlanList().forEach(x ->
x.setEmployerProducts(employerProduct));
}
// check to see if branding needs to be NULL. It will create the object from the select parameter with no id
// if a branding record has not been selected
if (emp.getBranding() != null && emp.getBranding().getBrandingNo() == null) {
emp.setBranding(null);
}
}
}
Employer DAO
#Repository
#Transactional
#Service
#Log4j
public class EmployerDao extends WebexchangeBaseDao implements IEmployerDao {
private Criteria criteria() {
return getCurrentSession().createCriteria(Employer.class);
}
#Override
#Transactional(readOnly = true)
public Employer read(Serializable id) {
return (Employer)getCurrentSession().load(Employer.class, id);
}
#Override
public Employer create(Employer employer) {
getCurrentSession().persist(employer);
return employer;
}
#Override
public Employer update(Employer employer) {
getCurrentSession().merge(employer);
return employer;
}
}
As of right now, my solution is to loop through the EmployerProducts and check for new records. I called a persist on the new ones before calling the merge() on the parent Employer. I also moved the logic I had associating all the keys into the dao instead of having it in my Struts action. Below is what my update() method in the Employer DAO now looks like
public Employer update(Employer employer) {
// associate this employer record with any products attached
for (EmployerProducts employerProduct : employer.getEmployerProductsList()) {
employerProduct.setEmployer(employer);
if (employerProduct.getEmployerProductsNo() == null) {
// The cascade down to employerProductsPlanList has issues getting the employerProductsNo
// automatically if the employerProduct does not exists yet. Persist the new employer product
// before we try to insert the new composite key in the plan
// https://stackoverflow.com/questions/54517061/hibernate-4-3-cascade-merge-through-multiple-lists-with-embeded-id
List<EmployerProductsPlan> plansToBeSaved = employerProduct.getEmployerProductsPlanList();
employerProduct.setEmployerProductsPlanList(new ArrayList<>());
getCurrentSession().persist(employerProduct);
// add the plans back in
employerProduct.setEmployerProductsPlanList(plansToBeSaved);
}
// associate the plan with the employer product
employerProduct.getEmployerProductsPlanList().forEach(x ->
x.getId().setEmployerProductsNo(employerProduct.getEmployerProductsNo())
);
}
return (Employer)getCurrentSession().merge(employer);
}

Jackson ObjectMapper Hibernate issue

I am working on a Spring Boot 2.0 / Java 8 shopping cart online application. I use Hibernate as the ORM framework.
I have two entities, Order and OrderDetail shown below:
#Entity
#Table(name = "orders")
public class Order extends AbstractEntityUuid {
#Column(name = "order_number", unique = true)
private String orderNumber;
#JsonBackReference
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "user_id", nullable = false)
private User user;
#Column(name = "total_amount")
private BigDecimal totalAmount = BigDecimal.ZERO;
#CreatedDate
#Column(name = "created_on", columnDefinition = "DATETIME", updatable = false)
protected LocalDateTime created;
#OneToMany(mappedBy = "order", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
#JsonManagedReference
private Set<OrderDetail> items = new HashSet<>();
#OneToOne(fetch = FetchType.LAZY, orphanRemoval = true)
#JoinColumn(nullable = false, name = "card_details_id")
private CardDetails card;
#OneToOne(fetch = FetchType.LAZY, orphanRemoval = true)
#JoinColumn(nullable = false, name = "shipping_address_id")
private Address shippingAddress;
#OneToOne(fetch = FetchType.LAZY, orphanRemoval = true)
#JoinColumn(name = "billing_address_id")
private Address billingAddress;
//getters and setters
}
#Entity
#Table(name = "order_detail")
public class OrderDetail extends AbstractPersistable<Long> {
#Column(name = "quantity")
private Integer quantity;
#Column(name = "total_amount")
private BigDecimal totalAmount = BigDecimal.ZERO;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "order_id", nullable = false)
#JsonBackReference
private Order order;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "product_id", nullable = false)
private Product product;
//getters and setters
}
When the user heads over to his Orders, he should be able to see information related only to the order itself (no details).
For that reason, I retrieve data only from the order table. Following is my repository:
public interface OrderRepository extends CrudRepository<Order, Long> {
#Query("FROM Order o WHERE o.user.email = ?1")
List<Order> findOrdersByUser(String email);
}
In the service, what I do is simply calling the above method and converting to a the dto counterpart.
#Transactional(readOnly = true)
public List<OrdersPreviewDTO> getOrdersPreview(String email) {
List<Order> orders = orderRepository.findOrdersByUser(email);
return orderConverter.convertToOrderPreviewDTOs(orders);
}
The converter uses an Jackson ObjectMapper object under the hood.
List<OrdersPreviewDTO> convertToOrderPreviewDTOs(List<Order> orders) {
return orders.stream()
.map(o -> objectMapper.convertValue(o, OrdersPreviewDTO.class))
.collect(toList());
}
The objectMapper is inject by Spring and defined in a configuration class:
#Bean
public ObjectMapper objectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.findAndRegisterModules();
return objectMapper;
}
The OrdersPreviewDTO dto object contains just a subset of the Order entity, because as I already mentioned, in the Orders page I want to show only high level properties of the user's orders, and nothing related to their details.
#JsonIgnoreProperties(ignoreUnknown = true)
public class OrdersPreviewDTO {
private String orderNumber;
#JsonFormat(pattern = "dd/MM/yyyy HH:mm")
private LocalDateTime created;
private BigDecimal totalAmount;
#JsonCreator
public OrdersPreviewDTO(
#JsonProperty("orderNumber") String orderNumber,
#JsonProperty("created") LocalDateTime created,
#JsonProperty("totalAmount") BigDecimal totalAmount) {
this.orderNumber = orderNumber;
this.created = created;
this.totalAmount = totalAmount;
}
//getters and setters
}
Everything works fine, the order entity is converted automagically by Jackson into its dto counterpart.
The problem come out when looking at the query executed by Hibernate under the hood.
Hibernate unwrap the collection of order details for each order and execute a query to retrieve data from each child collection:
select carddetail0_.id as id1_2_0_, carddetail0_.brand as brand2_2_0_, carddetail0_.created as created3_2_0_, carddetail0_.exp_month as exp_mont4_2_0_, carddetail0_.exp_year as exp_year5_2_0_, carddetail0_.last4 as last6_2_0_ from card_details carddetail0_ where carddetail0_.id=?
select address0_.id as id1_1_0_, address0_.created as created2_1_0_, address0_.last_modified as last_mod3_1_0_, address0_.city as city4_1_0_, address0_.country as country5_1_0_, address0_.first_name as first_na6_1_0_, address0_.last_name as last_nam7_1_0_, address0_.postal_code as postal_c8_1_0_, address0_.state as state9_1_0_, address0_.street_address as street_10_1_0_, address0_.telephone as telepho11_1_0_ from address address0_ where address0_.id=?
select items0_.order_id as order_id4_4_0_, items0_.id as id1_4_0_, items0_.id as id1_4_1_, items0_.order_id as order_id4_4_1_, items0_.product_id as product_5_4_1_, items0_.quantity as quantity2_4_1_, items0_.total_amount as total_am3_4_1_ from order_detail items0_ where items0_.order_id=?
and tons of others more.
Whether I modify the code in the following way, Hibernate runs only the expected query on the Order table:
This line of code:
objectMapper.convertValue(o, OrdersPreviewDTO.class)
is replaced by the following dirty fix:
new OrdersPreviewDTO(o.getOrderNumber(), o.getCreated(), o.getTotalAmount())
Query run by Hibernate:
select order0_.id as id1_5_, order0_.billing_address_id as billing_6_5_, order0_.card_details_id as card_det7_5_, order0_.created_on as created_2_5_, order0_.one_address as one_addr3_5_, order0_.order_number as order_nu4_5_, order0_.shipping_address_id as shipping8_5_, order0_.total_amount as total_am5_5_, order0_.user_id as user_id9_5_
from orders order0_ cross join user user1_
where order0_.user_id=user1_.id and user1_.user_email=?
My question is. Is there a way to tell Jackson to map only the Dtos field so that it doesn't trigger lazy loading fetches through Hibernate for the non required fields?
Thank you
The short answer is no, don't try and be so clever. Manually create your DTO to control any lazy loading, then use Jackson on the DTO outside the transaction.
The Long answer is yes, you can override MappingJackson2HttpMessageConverter and control which fields get called from the entity.
#Configuration
public class MixInWebConfig extends WebMvcConfigurationSupport {
#Bean
public MappingJackson2HttpMessageConverter customJackson2HttpMessageConverter2() {
MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.addMixIn(DTO1.class, FooMixIn.class);
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
jsonConverter.setObjectMapper(objectMapper);
return jsonConverter;
}
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(customJackson2HttpMessageConverter2());
}
}
Then
#Override
protected void processViews(SerializationConfig config, BeanSerializerBuilder builder) {
super.processViews(config, builder);
if (classes.contains(builder.getBeanDescription().getBeanClass())) {
List<BeanPropertyWriter> originalWriters = builder.getProperties();
List<BeanPropertyWriter> writers = new ArrayList<BeanPropertyWriter>();
for (BeanPropertyWriter writer : originalWriters) {
String propName = writer.getName();
if (!fieldsToIgnore.contains(propName)) {
writers.add(writer);
}
}
builder.setProperties(writers);
}
}
}
here is a working example.
+1 for Essex Boy answer. I just want to add that you can directly return DTO from your JPQL Queries instead of using Jackson. It avoids a transformation from the database to your object Order and then another transformation from Order object to OrdersPreviewDTO object.
For example, you need to change your query in your repository to do it. It would be something like :
public interface OrderRepository extends CrudRepository<Order, Long> {
#Query("SELECT new OrdersPreviewDTO(o.order_number, o.created_on, o.total_amount)) FROM Order o WHERE o.user.email = ?1")
List<OrdersPreviewDTO> findOrdersByUser(String email);
}
If OrdersPreviewDTO is strictly a subset of your Order class, why not simply use the #JsonView annotation to automatically create a simple view in your controller? See https://spring.io/blog/2014/12/02/latest-jackson-integration-improvements-in-spring for example.
In case you need a DTO for both input and output, also consider using http://mapstruct.org/

Jackson - couldn't write json - infinite recurison [duplicate]

When trying to convert a JPA object that has a bi-directional association into JSON, I keep getting
org.codehaus.jackson.map.JsonMappingException: Infinite recursion (StackOverflowError)
All I found is this thread which basically concludes with recommending to avoid bi-directional associations. Does anyone have an idea for a workaround for this spring bug?
------ EDIT 2010-07-24 16:26:22 -------
Codesnippets:
Business Object 1:
#Entity
#Table(name = "ta_trainee", uniqueConstraints = {#UniqueConstraint(columnNames = {"id"})})
public class Trainee extends BusinessObject {
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
#Column(name = "id", nullable = false)
private Integer id;
#Column(name = "name", nullable = true)
private String name;
#Column(name = "surname", nullable = true)
private String surname;
#OneToMany(mappedBy = "trainee", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#Column(nullable = true)
private Set<BodyStat> bodyStats;
#OneToMany(mappedBy = "trainee", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#Column(nullable = true)
private Set<Training> trainings;
#OneToMany(mappedBy = "trainee", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#Column(nullable = true)
private Set<ExerciseType> exerciseTypes;
public Trainee() {
super();
}
//... getters/setters ...
}
Business Object 2:
import javax.persistence.*;
import java.util.Date;
#Entity
#Table(name = "ta_bodystat", uniqueConstraints = {#UniqueConstraint(columnNames = {"id"})})
public class BodyStat extends BusinessObject {
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
#Column(name = "id", nullable = false)
private Integer id;
#Column(name = "height", nullable = true)
private Float height;
#Column(name = "measuretime", nullable = false)
#Temporal(TemporalType.TIMESTAMP)
private Date measureTime;
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name="trainee_fk")
private Trainee trainee;
}
Controller:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletResponse;
import javax.validation.ConstraintViolation;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
#Controller
#RequestMapping(value = "/trainees")
public class TraineesController {
final Logger logger = LoggerFactory.getLogger(TraineesController.class);
private Map<Long, Trainee> trainees = new ConcurrentHashMap<Long, Trainee>();
#Autowired
private ITraineeDAO traineeDAO;
/**
* Return json repres. of all trainees
*/
#RequestMapping(value = "/getAllTrainees", method = RequestMethod.GET)
#ResponseBody
public Collection getAllTrainees() {
Collection allTrainees = this.traineeDAO.getAll();
this.logger.debug("A total of " + allTrainees.size() + " trainees was read from db");
return allTrainees;
}
}
JPA-implementation of the trainee DAO:
#Repository
#Transactional
public class TraineeDAO implements ITraineeDAO {
#PersistenceContext
private EntityManager em;
#Transactional
public Trainee save(Trainee trainee) {
em.persist(trainee);
return trainee;
}
#Transactional(readOnly = true)
public Collection getAll() {
return (Collection) em.createQuery("SELECT t FROM Trainee t").getResultList();
}
}
persistence.xml
<persistence 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_1_0.xsd"
version="1.0">
<persistence-unit name="RDBMS" transaction-type="RESOURCE_LOCAL">
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property name="hibernate.hbm2ddl.auto" value="validate"/>
<property name="hibernate.archive.autodetection" value="class"/>
<property name="dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect"/>
<!-- <property name="dialect" value="org.hibernate.dialect.HSQLDialect"/> -->
</properties>
</persistence-unit>
</persistence>
JsonIgnoreProperties [2017 Update]:
You can now use JsonIgnoreProperties to suppress serialization of properties (during serialization), or ignore processing of JSON properties read (during deserialization). If this is not what you're looking for, please keep reading below.
(Thanks to As Zammel AlaaEddine for pointing this out).
JsonManagedReference and JsonBackReference
Since Jackson 1.6 you can use two annotations to solve the infinite recursion problem without ignoring the getters/setters during serialization: #JsonManagedReference and #JsonBackReference.
Explanation
For Jackson to work well, one of the two sides of the relationship should not be serialized, in order to avoid the infite loop that causes your stackoverflow error.
So, Jackson takes the forward part of the reference (your Set<BodyStat> bodyStats in Trainee class), and converts it in a json-like storage format; this is the so-called marshalling process. Then, Jackson looks for the back part of the reference (i.e. Trainee trainee in BodyStat class) and leaves it as it is, not serializing it. This part of the relationship will be re-constructed during the deserialization (unmarshalling) of the forward reference.
You can change your code like this (I skip the useless parts):
Business Object 1:
#Entity
#Table(name = "ta_trainee", uniqueConstraints = {#UniqueConstraint(columnNames = {"id"})})
public class Trainee extends BusinessObject {
#OneToMany(mappedBy = "trainee", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#Column(nullable = true)
#JsonManagedReference
private Set<BodyStat> bodyStats;
Business Object 2:
#Entity
#Table(name = "ta_bodystat", uniqueConstraints = {#UniqueConstraint(columnNames = {"id"})})
public class BodyStat extends BusinessObject {
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name="trainee_fk")
#JsonBackReference
private Trainee trainee;
Now it all should work properly.
If you want more informations, I wrote an article about Json and Jackson Stackoverflow issues on Keenformatics, my blog.
EDIT:
Another useful annotation you could check is #JsonIdentityInfo: using it, everytime Jackson serializes your object, it will add an ID (or another attribute of your choose) to it, so that it won't entirely "scan" it again everytime. This can be useful when you've got a chain loop between more interrelated objects (for example: Order -> OrderLine -> User -> Order and over again).
In this case you've got to be careful, since you could need to read your object's attributes more than once (for example in a products list with more products that share the same seller), and this annotation prevents you to do so. I suggest to always take a look at firebug logs to check the Json response and see what's going on in your code.
Sources:
Keenformatics - How To Solve JSON infinite recursion Stackoverflow (my blog)
Jackson References
Personal experience
You may use #JsonIgnore to break the cycle (reference).
You need to import org.codehaus.jackson.annotate.JsonIgnore (legacy versions) or com.fasterxml.jackson.annotation.JsonIgnore (current versions).
The new annotation #JsonIgnoreProperties resolves many of the issues with the other options.
#Entity
public class Material{
...
#JsonIgnoreProperties("costMaterials")
private List<Supplier> costSuppliers = new ArrayList<>();
...
}
#Entity
public class Supplier{
...
#JsonIgnoreProperties("costSuppliers")
private List<Material> costMaterials = new ArrayList<>();
....
}
Check it out here. It works just like in the documentation:
http://springquay.blogspot.com/2016/01/new-approach-to-solve-json-recursive.html
Also, using Jackson 2.0+ you can use #JsonIdentityInfo. This worked much better for my hibernate classes than #JsonBackReference and #JsonManagedReference, which had problems for me and did not solve the issue. Just add something like:
#Entity
#Table(name = "ta_trainee", uniqueConstraints = {#UniqueConstraint(columnNames = {"id"})})
#JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="#traineeId")
public class Trainee extends BusinessObject {
#Entity
#Table(name = "ta_bodystat", uniqueConstraints = {#UniqueConstraint(columnNames = {"id"})})
#JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="#bodyStatId")
public class BodyStat extends BusinessObject {
and it should work.
Also, Jackson 1.6 has support for handling bi-directional references... which seems like
what you are looking for (this blog entry also mentions the feature)
And as of July 2011, there is also "jackson-module-hibernate" which might help in some aspects of dealing with Hibernate objects, although not necessarily this particular one (which does require annotations).
This worked perfectly fine for me.
Add the annotation #JsonIgnore on the child class where you mention the reference to the parent class.
#ManyToOne
#JoinColumn(name = "ID", nullable = false, updatable = false)
#JsonIgnore
private Member member;
Now Jackson supports avoiding cycles without ignoring the fields:
Jackson - serialization of entities with birectional relationships (avoiding cycles)
Working fine for me
Resolve Json Infinite Recursion problem when working with Jackson
This is what I have done in oneToMany and ManyToOne Mapping
#ManyToOne
#JoinColumn(name="Key")
#JsonBackReference
private LgcyIsp Key;
#OneToMany(mappedBy="LgcyIsp ")
#JsonManagedReference
private List<Safety> safety;
For me the best solution is to use #JsonView and create specific filters for each scenario. You could also use #JsonManagedReference and #JsonBackReference, however it is a hardcoded solution to only one situation, where the owner always references the owning side, and never the opposite. If you have another serialization scenario where you need to re-annotate the attribute differently, you will not be able to.
Problem
Lets use two classes, Company and Employee where you have a cyclic dependency between them:
public class Company {
private Employee employee;
public Company(Employee employee) {
this.employee = employee;
}
public Employee getEmployee() {
return employee;
}
}
public class Employee {
private Company company;
public Company getCompany() {
return company;
}
public void setCompany(Company company) {
this.company = company;
}
}
And the test class that tries to serialize using ObjectMapper (Spring Boot):
#SpringBootTest
#RunWith(SpringRunner.class)
#Transactional
public class CompanyTest {
#Autowired
public ObjectMapper mapper;
#Test
public void shouldSaveCompany() throws JsonProcessingException {
Employee employee = new Employee();
Company company = new Company(employee);
employee.setCompany(company);
String jsonCompany = mapper.writeValueAsString(company);
System.out.println(jsonCompany);
assertTrue(true);
}
}
If you run this code, you'll get the:
org.codehaus.jackson.map.JsonMappingException: Infinite recursion (StackOverflowError)
Solution Using `#JsonView`
#JsonView enables you to use filters and choose what fields should be included while serializing the objects. A filter is just a class reference used as a identifier. So let's first create the filters:
public class Filter {
public static interface EmployeeData {};
public static interface CompanyData extends EmployeeData {};
}
Remember, the filters are dummy classes, just used for specifying the fields with the #JsonView annotation, so you can create as many as you want and need. Let's see it in action, but first we need to annotate our Company class:
public class Company {
#JsonView(Filter.CompanyData.class)
private Employee employee;
public Company(Employee employee) {
this.employee = employee;
}
public Employee getEmployee() {
return employee;
}
}
and change the Test in order for the serializer to use the View:
#SpringBootTest
#RunWith(SpringRunner.class)
#Transactional
public class CompanyTest {
#Autowired
public ObjectMapper mapper;
#Test
public void shouldSaveCompany() throws JsonProcessingException {
Employee employee = new Employee();
Company company = new Company(employee);
employee.setCompany(company);
ObjectWriter writter = mapper.writerWithView(Filter.CompanyData.class);
String jsonCompany = writter.writeValueAsString(company);
System.out.println(jsonCompany);
assertTrue(true);
}
}
Now if you run this code, the Infinite Recursion problem is solved, because you have explicitly said that you just want to serialize the attributes that were annotated with #JsonView(Filter.CompanyData.class).
When it reaches the back reference for company in the Employee, it checks that it's not annotated and ignore the serialization. You also have a powerful and flexible solution to choose which data you want to send through your REST APIs.
With Spring you can annotate your REST Controllers methods with the desired #JsonView filter and the serialization is applied transparently to the returning object.
Here are the imports used in case you need to check:
import static org.junit.Assert.assertTrue;
import javax.transaction.Transactional;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.annotation.JsonView;
There's now a Jackson module (for Jackson 2) specifically designed to handle Hibernate lazy initialization problems when serializing.
https://github.com/FasterXML/jackson-datatype-hibernate
Just add the dependency (note there are different dependencies for Hibernate 3 and Hibernate 4):
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-hibernate4</artifactId>
<version>2.4.0</version>
</dependency>
and then register the module when intializing Jackson's ObjectMapper:
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new Hibernate4Module());
Documentation currently isn't great. See the Hibernate4Module code for available options.
#JsonIgnoreProperties is the answer.
Use something like this ::
#OneToMany(mappedBy = "course",fetch=FetchType.EAGER)
#JsonIgnoreProperties("course")
private Set<Student> students;
You Should use #JsonBackReference with #ManyToOne entity and #JsonManagedReference with #onetomany containing entity classes.
#OneToMany(
mappedBy = "queue_group",fetch = FetchType.LAZY,
cascade = CascadeType.ALL
)
#JsonManagedReference
private Set<Queue> queues;
#ManyToOne(cascade=CascadeType.ALL)
#JoinColumn(name = "qid")
// #JsonIgnore
#JsonBackReference
private Queue_group queue_group;
In my case it was enough to change relation from:
#OneToMany(mappedBy = "county")
private List<Town> towns;
to:
#OneToMany
private List<Town> towns;
another relation stayed as it was:
#ManyToOne
#JoinColumn(name = "county_id")
private County county;
I also met the same problem. I used #JsonIdentityInfo's ObjectIdGenerators.PropertyGenerator.class generator type.
That's my solution:
#Entity
#Table(name = "ta_trainee", uniqueConstraints = {#UniqueConstraint(columnNames = {"id"})})
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class Trainee extends BusinessObject {
...
VERY IMPORTANT: If you are using LOMBOK, make shure to exclude attributes of collections like Set, List, etc...
Like this:
#EqualsAndHashCode(exclude = {"attributeOfTypeList", "attributeOfTypeSet"})
Be sure you use com.fasterxml.jackson everywhere. I spent much time to find it out.
<properties>
<fasterxml.jackson.version>2.9.2</fasterxml.jackson.version>
</properties>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${fasterxml.jackson.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${fasterxml.jackson.version}</version>
</dependency>
Then use #JsonManagedReference and #JsonBackReference.
Finally, you can serialize your model to JSON:
import com.fasterxml.jackson.databind.ObjectMapper;
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(model);
You can use #JsonIgnore, but this will ignore the json data which can be accessed because of the Foreign Key relationship. Therefore if you reqiure the foreign key data (most of the time we require), then #JsonIgnore will not help you. In such situation please follow the below solution.
you are getting Infinite recursion, because of the BodyStat class again referring the Trainee object
BodyStat
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name="trainee_fk")
private Trainee trainee;
Trainee
#OneToMany(mappedBy = "trainee", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#Column(nullable = true)
private Set<BodyStat> bodyStats;
Therefore, you have to comment/omit the above part in Trainee
I have the same problem after doing more analysis i came to know that, we can get mapped entity also by just keeping #JsonBackReference at OneToMany annotation
#Entity
#Table(name = "ta_trainee", uniqueConstraints = {#UniqueConstraint(columnNames = {"id"})})
public class Trainee extends BusinessObject {
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
#Column(name = "id", nullable = false)
private Integer id;
#Column(name = "name", nullable = true)
private String name;
#Column(name = "surname", nullable = true)
private String surname;
#OneToMany(mappedBy = "trainee", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#Column(nullable = true)
#JsonBackReference
private Set<BodyStat> bodyStats;
you can use DTO pattern
create class TraineeDTO without any anotation hiberbnate and you can use jackson mapper to convert Trainee to TraineeDTO and bingo the error message disapeare :)
If you cannot ignore the property, try modifying the visibility of the field. In our case, we had old code still submitting entities with the relationship, so in my case, this was the fix:
#JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
private Trainee trainee;
For some reason, in my case, it wasn't working with Set. I had to change it to List and use #JsonIgnore and #ToString.Exclude to get it working.
Replace Set with List:
//before
#OneToMany(mappedBy="client")
private Set<address> addressess;
//after
#OneToMany(mappedBy="client")
private List<address> addressess;
And add #JsonIgnore and #ToString.Exclude annotations:
#ManyToOne
#JoinColumn(name="client_id", nullable = false)
#JsonIgnore
#ToString.Exclude
private Client client;
If you use #JsonManagedReference, #JsonBackReference or #JsonIgnore annotation it ignore some fields and solve Infinite Recursion with Jackson JSON.
But if you use #JsonIdentityInfo which also avoid the Infinite Recursion and you can get all the fields values, so I suggest that you use #JsonIdentityInfo annotation.
#JsonIdentityInfo(generator= ObjectIdGenerators.UUIDGenerator.class, property="#id")
Refer this article https://www.toptal.com/javascript/bidirectional-relationship-in-json to get good understanding about #JsonIdentityInfo annotation.
This post: https://www.baeldung.com/jackson-bidirectional-relationships-and-infinite-recursion
has a full explanation.
If you are using Jackson with older versions, you can try #jsonmanagedreference + #jsonbackreference. If your Jackson is above 2 (1.9 also doesn't work as I know), try #JsonIdentityInfo instead.
As someone using Spring Data and Lombok, this is how I solved it for myself.
#Entity
#Data
public class Foo extends BaseEntity {
#OneToMany(fetch = FetchType.EAGER)
#JoinColumn(name = "foo_id")
#JsonIgnoreProperties("parent_foo")
#EqualsAndHashCode.Exclude
private Set<Bar> linkedBars;
}
#Entity
#Data
public class Bar extends BaseEntity {
#Column(name = "foo_id")
private Long parentFooId;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "foo_id", insertable = false, updatable = false)
#JsonIgnoreProperties({"linkedBars"})
private Foo parentFoo;
}
The JsonIgnoreProperties annotation stops infinite recursion as many answers have discussed above.
#EqualsAndHashCode.Exclude prevents the StackOverflowError caused by hashCode and equals being called recursively.
Using Set over List resolves the MultipleBagFetchException which occurs when you add multiple collection fields. You can also use #Fetch(value = FetchMode.SUBSELECT) to avoid the cartesian product, but I haven't tried it personally since my use case didn't need it.
The explicit definition of parentFooId in Bar is to allow mapping Foo entities with Bars.
I had this problem, but I didn't want to use annotation in my entities, so I solved by creating a constructor for my class, this constructor must not have a reference back to the entities who references this entity. Let's say this scenario.
public class A{
private int id;
private String code;
private String name;
private List<B> bs;
}
public class B{
private int id;
private String code;
private String name;
private A a;
}
If you try to send to the view the class B or A with #ResponseBody it may cause an infinite loop. You can write a constructor in your class and create a query with your entityManager like this.
"select new A(id, code, name) from A"
This is the class with the constructor.
public class A{
private int id;
private String code;
private String name;
private List<B> bs;
public A(){
}
public A(int id, String code, String name){
this.id = id;
this.code = code;
this.name = name;
}
}
However, there are some constrictions about this solution, as you can see, in the constructor I did not make a reference to List bs this is because Hibernate does not allow it, at least in version 3.6.10.Final, so when I need to show both entities in a view I do the following.
public A getAById(int id); //THE A id
public List<B> getBsByAId(int idA); //the A id.
The other problem with this solution, is that if you add or remove a property you must update your constructor and all your queries.
In case you are using Spring Data Rest, issue can be resolved by creating Repositories for every Entity involved in cyclical references.
I'm a late comer and it's such a long thread already. But I spent a couple of hours trying to figure this out too, and would like to give my case as another example.
I tried both JsonIgnore, JsonIgnoreProperties and BackReference solutions, but strangely enough it was like they weren't picked up.
I used Lombok and thought that maybe it interferes, since it creates constructors and overrides toString (saw toString in stackoverflowerror stack).
Finally it wasn't Lombok's fault - I used automatic NetBeans generation of JPA entities from database tables, without giving it much thought - well, and one of the annotations that were added to the generated classes was #XmlRootElement. Once I removed it everything started working. Oh well.
The point is to place the #JsonIgnore in the setter method as follow. in my case.
Township.java
#Access(AccessType.PROPERTY)
#OneToMany(fetch = FetchType.LAZY)
#JoinColumn(name="townshipId", nullable=false ,insertable=false, updatable=false)
public List<Village> getVillages() {
return villages;
}
#JsonIgnore
#Access(AccessType.PROPERTY)
public void setVillages(List<Village> villages) {
this.villages = villages;
}
Village.java
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "townshipId", insertable=false, updatable=false)
Township township;
#Column(name = "townshipId", nullable=false)
Long townshipId;
I have faced same issue, add jsonbackref and jsonmanagedref and please make sure #override equals and hashCode methods , this definitely fix this issue.

Categories