I'm having a lazy initialization exception using spring. I know this is a common error and I've been through several stack questions, but none of the answers really did it for me. Here's my scenario: I have two classes that relate like such:
public class Foo implements Serializable {
#Id
#EqualsAndHashCode.Include
#Column(name = "uuid")
private UUID uuid;
#Column(name = “attribute”)
private String attribute;
#OneToMany(fetch = FetchType.LAZY, mappedBy = “foo”)
private Set<Bar> bar;
}
public class Bar implements Serializable {
#Id
#Column(name = "uuid")
private UUID uuid;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = “foo_uuid")
private Foo foo;
}
I have a rest endpoint that lists all Bar objects. In that specific endpoint, I need to return attribute, which is a Foo attribute. Since that is not required inside the application business logic, it seemed unnecessary to add attribute to Bar as well. So I have a BarWrapper class, which is a custom format of Bar, to be returned by the endpoint:
#Getter
#Setter
#NoArgsConstructor
public class BarWrapper {
…
private String attribute;
public BarWrapper(final Bar bar) {
//The next line throws lazy initialization exception.
this.attribute = bar.getFoo().getAttribute()
}
I have tried #Transactional on all classes, and didn't work. I tried to add cascade = CascadeType.ALL, which did work, but is not a good practice. I have also tried creating a custom function just for this, but didn't help either:
#Transactional
private String extractAttribute(final Bar bar){
final Foo foo = bar.getFoo();
return foo.getAttribute();
}
How can I overcome this Lazy initialization exception?
EDIT:
This is how I'm calling the BarWrapper constructor:
#AllArgsConstructor
#Service
#Slf4j
public class BarApplicationServices {
private final FooService fooService;
private final BarService barService;
public BarWrapper createBar(final CreateBarRequestBody requestBody) {
final Foo foo = fooService.findFooToBeSettled(requestBody.getFooUuid());
final Bar createdBar = barService
.createBar(new Bar(foo));
return new BarWrapper(createdBar);
}
}
Your #Transactional method is private which does not work. Make it public.
If this does not work, try the following link
Related
I want to create a unit test that will use reflection to find all missing fields in dto that implement BaseDto by their persistence entities. This is what I did.
#Slf4j
public class EntityAuditDtoTest {
#Test
public void find_MissingAndExtraFieldsThatUsedInAuditDtosByEntity_ReturnMissingAndExtraFields() throws ClassNotFoundException {
// Arrange
ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
scanner.addIncludeFilter(new AnnotationTypeFilter(AuditEntityType.class));
// Find all classes annotated with #AuditEntityType in the package com.example.dto
Set<BeanDefinition> auditDtoBeans = scanner.findCandidateComponents("com.example.dto");
// Act
for (BeanDefinition auditDtoBean : auditDtoBeans) {
Class<?> auditDtoClass = Class.forName(auditDtoBean.getBeanClassName());
// Make sure the DTO class implements BaseAuditDto
if (!BaseAuditDto.class.isAssignableFrom(auditDtoClass)) {
continue;
}
Class<?> entityClass = getEntityClassForDto(auditDtoClass);
Field[] dtoFields = auditDtoClass.getDeclaredFields();
Field[] entityFields = entityClass.getDeclaredFields();
List<String> missingFields = Arrays.stream(entityFields).map(Field::getName)
.filter(field -> Arrays.stream(dtoFields).noneMatch(f -> f.getName().equals(field))).toList();
if (!missingFields.isEmpty()) {
log.error("Missing fields in DTO class: {} \nfor entity class: {} : {}", auditDtoClass.getName(),
entityClass.getName(), missingFields);
}
List<String> extraFields = Arrays.stream(dtoFields).map(Field::getName)
.filter(field -> Arrays.stream(entityFields).noneMatch(f -> f.getName().equals(field))).toList();
if (!extraFields.isEmpty()) {
log.error("Extra fields in DTO class: {} \nfor entity class: {} : {}", auditDtoClass.getName(),
entityClass.getName(), extraFields);
}
}
}
}
But the problem is that the dto may have a field that is in the entity class, but the test will think that this is a missing field.
For example:
Dto class: ContractAudit has customerId field (customerId). And ContractEntity has public CustomerEntity customer.
This is the same fields. But of course for test they are different. I don't understand how to ignore them. I also don't want to hardcode filter that skip all endings with 'id' prefix.
#Data
#AuditEntityType("Contract")
public class ContractAudit implements BaseAuditDto {
private Long id;
private String ref;
private String status;
private Long customerId;
}
#Entity
#Table(name = "contract")
#Getter
#Setter
#ToString
#AllArgsConstructor
#NoArgsConstructor
#Builder
public class ContractEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
#ToString.Include
private Long id;
#Column(name = "ref", updatable = true)
#ToString.Include
private String ref;
#Column(name = "status")
#ToString.Include
#Enumerated(value = EnumType.STRING)
private ContractStatusEnum status;
#ManyToOne
#JoinColumn(name = "customer_id")
public CustomerEntity customer;
#Column(name = "deleted")
#ToString.Include
private boolean deleted;
#OneToMany(fetch = FetchType.LAZY)
#JoinColumn(name = "contract_id")
private List<ContractDocumentEntity> documents;
}
Output:
Missing fields in DTO class: ContractAudit for entity class: ContractEntity : [customer, deleted, documents]
Extra fields in DTO class: ContractAudit for entity class: ContractEntity : [customerId]
I want to have missing fields: [deleted, documents]
If you have any other ideas on how to do this, I'd love to hear it. I am not asking for implementation. Suggestions only)
Lol. I found solution for my case.
My previous approach was incorrect. Because it's impossible to find 'missing' and 'extra' fields by name correctly for every case. I decided to use:
assertThat(entityClass.getDeclaredFields()).hasSameSizeAs(auditDtoClass.getDeclaredFields());
So this code is checking if the entityClass and the DtoClass have the same number of fields (properties) declared. If not it fail test and print all fields from each classes. If anyone has better ideas I'll be happy to hear.
I am developint simple Crud Application in Spring Boot with Data JPA, my goal is simple where I have two Entities: Foo.java
#Data // lombok annotation
#Entity(name = "foos")
public class Foo{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(unique = true, nullable = false)
private Integer fooId;
private String FooName;
#NonNull
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "bar_id")
private List<Bar> barList;
}
Bar.java
#Data
#Entity(name = "bars")
public class Bar{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id", unique = true, nullable = false)
private Integer barId;
private String barTitle;
}
In my controller I want to save Foo with List of Bars as:
#Controller
#RequestMapping(value = "/foo")
public class FooController {
private final FooService fooService;
private final BarService barService;
public FooController(FooService fooService, BarService barService) {
this.fooService = fooService;
this.barService = barService;
}
#GetMapping(value = "/{id}/add_bar")
public String addBar(#PathVariable("id") Integer id, Model model){
model.addAttribute("foo", fooService.findById(id));
model.addAttribute("bar", new Bar());
return "add_bar";
}
#PostMapping(value = "/{id}/add_bar")
public String saveBar(
#PathVariable("id") Integer id,
#ModelAttribute("bar") Bar bar,
BindingResult result
){
if (result.hasErrors()) {
return "add_bar";
}
// update foo by adding new bar and save
Foo foo = getFooAndAddBar(id, bar);
fooService.save(foo);
// save bar
barService.save(bar);
return "redirect:/foo/" + foo.getFooId();
}
// update foo by adding new bar and save
private Foo getFooAndAddBar(Integer id, Bar bar) {
Foo foo = fooService.findById(id);
ArrayList<Bar> barList = new ArrayList<>();
barList.add(bar);
foo.setBarList(barList);
return foo;
}
}
First bar is saved and fetched by foo id, but when ever I want to add another bar it only updates the first bar instead of inserting a new bar record in DB. is there any thing missing for #OneToMany association? or something is missing in other part of the program? please.
you are setting your barlistevery time you are calling your function. You have written foo.setBarList(barList);. every time this will overwrite the previous barlist and then save it resulting in overwriting the previous values. Instead of this try this foo.getBarList().add(Bar). It will fetch the previous list of bar and then will add new bar to the list. After this just save the entity
When I return a Page<Entity> from a method inside my #RestController class, all fields of Entity both referenced via #OneToXXX and #ManyToXXX take place in the returned JSON object. But when I switched the return type to PagedResource (to be able to add links to the response), #ManyToXXX fields are not included at all.
Here is the method in question:
#GetMapping("/fetch")
public PagedResources getResults(Pageable pageable, PagedResourcesAssembler assembler) {
Page<ParentClass> page = myRepository.findAll(pageable);
PagedResources pagedResources = assembler.toResource(page, myResourceAssembler);
return pagedResources;
}
Here is the resource assembler: it's #Autowired in the MyController's body.
MyResourceAssembler
#Component
public class MyResourceAssembler extends ResourceAssemblerSupport<ParentClass, Resource> {
public MyResourceAssembler() { super(MyController.class, Resource.class); }
#Override
public Resource toResource(ParentClass obj) {
return new Resource<>(obj,
linkTo(methodOn(MyController.class).getResults(obj.getId())).withRel("edit"),
}
}
Here are the basic class definitions:
ParentClass
#Entity
#Table(name = "parent_table", catalog = "myDB")
public class ParentClass implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false)
private Integer id;
#ManyToOne(optional = false)
#JoinColumn(name = "other_class", referencedColumnName = "id")
private OtherClass otherClass;
#OneToOne(mappedBy = "parent")
private SampleField1 sampleField1;
#OneToMany(mappedBy = "parent")
private List<SampleField2> sampleField2;
}
SampleField1 OneToXXX
#Entity
#Table(name = "sample_table_1", catalog = "myDB")
public class SampleField1 implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false)
private Integer id;
#Column(name="some_field")
String someField;
#OneToOne
#JoinColumn(name = "sample_field_1", referencedColumnName = "id")
#JsonBackReference //to avoid infinite recursion
private ParentClass parent;
}
OtherClass ManyToOne
#Entity
#Table(name = "other_table", catalog = "myDB")
public class OtherClass implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false)
private Integer id;
#Column(name="some_other_field")
String someOtherField;
// I don't need any reference to ParentClass here.
}
To add further detail to the issue here is the logging output of changeProperties() method inside PersistentEntityJackson2Module class:
s.d.r.w.j.PersistentEntityJackson2Module : Assigning nested entity serializer for #javax.persistence.OneToOne(..) com.project.SampleField1 com.project.model.ParentClass.sampleField1
s.d.r.w.j.PersistentEntityJackson2Module : Assigning nested entity serializer for #javax.persistence.OneToMany(..) com.project.SampleField2 com.project.model.ParentClass.sampleField2
// .... omitted other lines for brevity
the resulting JSON is :
{
"_embedded":{
"parentClasses":[
{
"id":1,
// <-- There is no field for otherClass !
"sampleField1":{
"id":1,
"sampleField":"blabla"
},
"sampleField2":[ ]
}
]
},
"links":[
]
}
As it can be seen above, OneToXXX fields are being taken to be serialized but no output for the ManyToOne fields like
Assigning nested entity serializer for #javax.persistence.ManyToOne ... com.my.OtherClass ... and therefore those aren't existed in the response JSON.
According to this SO answer, #ManyToXXX referenced entities are appended as links to the JSON response. But that's not an acceptable solution for me since I have a different planning of consumption in my mind for the rest client.
Bottomline, I'd like to have my ManyToOne referenced entities in my JSON Response returned from getResults() method.
Anything I can provide just ask in the comments.
Return Entity in responses is not the best way, because usually clients dont need whole set of data. Also, if Entities has links for each other, it will cause StackoverflowException on serialization tries. Use DTO for responses. At least it will help you to determine where is the problem - serialization, or fetching from database. Anyway it is more proper way for serving data to clients.
By the way, check getter and setter for otherClass in your ParentClass :) If threre is no getter and setter, thats will be reason of your issue.
Also, take a look into OtherClass for default empty constructor. If it hasn't present in there, you should add it.
I have tried several things I found while searching but nothing helped or I did not implement it correctly.
Error I'm getting
Direct self-reference leading to cycle (through reference chain: io.test.entity.bone.Special["appInstance"]->io.test.entity.platform.ApplicationInstance["appInstance"])
Both these extend the base entity and in the base (super class) it has an appInstance as well.
Base entity looks similar to this
#MappedSuperclass
public abstract class BaseEntity implements Comparable, Serializable {
#ManyToOne
protected ApplicationInstance appInstance;
//getter & setter
}
Application entity looks like this
public class ApplicationInstance extends BaseEntity implements Serializable {
private List<User> users;
// some other properties (would all have the same base and application instance . User entity will look similar to the Special.)
}
Special entity
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "objectType")
#JsonIgnoreProperties({"createdBy", "appInstance", "lastUpdatedBy"})
public class Special extends BaseEntity implements Serializable {
#NotNull
#Column(nullable = false)
private String name;
#Column(length = Short.MAX_VALUE)
private String description;
#NotNull
#Column(nullable = false)
private Double price;
#OneToOne
private Attachment image;
#Enumerated(EnumType.STRING)
#ElementCollection(targetClass = SpecialTag.class)
#CollectionTable(name = "special_tags")
#Column(name = "specialtag")
private List<SpecialTag> specialTags;
#Temporal(TemporalType.TIME)
private Date specialStartTime;
#Temporal(TemporalType.TIME)
private Date specialEndTime;
#Enumerated(EnumType.STRING)
#ElementCollection(targetClass = WeekDay.class)
#CollectionTable(name = "available_week_days")
#Column(name = "weekday")
private List<WeekDay> availableWeekDays;
#OneToMany(mappedBy = "special", cascade = CascadeType.REFRESH)
private List<SpecialStatus> statuses;
#OneToMany(mappedBy = "special", cascade = CascadeType.REFRESH)
private List<SpecialReview> specialReviews;
#Transient
private Integer viewed;
private Boolean launched;
#OneToMany(mappedBy = "special")
private List<CampaignSpecial> specialCampaigns;
#Override
#JsonIgnore
public ApplicationInstance getAppInstance() {
return super.getAppInstance();
}
}
All entities in Special inherits from BaseEntity which contains AppInstance
then i have a method to get the special
#GET
#Path("{ref}")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(value = MediaType.TEXT_PLAIN)
public Special findByGuestRef(#PathParam("ref") String pRefeference) {
// find the special and return it
return special;
}
On the special entity I tried the following
Added jsonIgnoreProperties
Added an override for appInstance to annotate with #JsonIgnore
#JsonIdentityInfo
links for the above
https://stackoverflow.com/a/29632358/4712391
Jackson serialization: how to ignore superclass properties
jackson self reference leading to cycle
none of those solutions works. Am I doing something wrong?
Note: Would it also just be possible to edit special, since the other entities are in a different package and would not like to edit them.
Usually excluding attributes in a response is as easy as adding a #JsonIgnore annotation to their getters, but if you don't want to add this annotation to a parent class, you could override the getter and then add the annotation on it:
public class Special extends BaseEntity implements Serializable {
...
#JsonIgnore
public ApplicationInstance getAppInstance() {
return this.appInstance;
}
...
}
NOTE: As there are several frameworks, make sure that you are using the correct #JsonIgnore annotation or it will be ignored, see this answer for instance.
Another option, more "manual", is just creating a bean for the response which would be a subset of the Special instance:
#GET
#Path("{ref}")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(value = MediaType.TEXT_PLAIN)
public SpecialDTO findByGuestRef(#PathParam("ref") String pRefeference) {
// find the special and return it
return new SpecialDTO(special);
}
public class SpecialDTO {
//declare here only the attributes that you want in your response
public SpecialDTO(Special sp) {
this.attr=sp.attr; // populate the needed attributes
}
}
To me, problem seems to be in the Special object and the fields being initialized in it.
I guess that there is a circular reference detected when serialisation happens.
Something similar to:
class A {
public A child;
public A parent;
}
A object = new A();
A root = new A();
root.child = object;
object.parent = root;
In the above code, whenever you will try to seralize either of these objects, you will face the same problem.
Note that public fields are not recommended.
I'll suggest to peek into your Special object and the references set in it.
I am trying out some very basic webservice. I get this exception everytime I try to return the Prtnr object.
Uncaught exception thrown in one of the service methods of the servlet: spitter. Exception thrown :
org.codehaus.jackson.map.JsonMappingException: Infinite recursion (StackOverflowError)
(through reference chain: org.hibernate.collection.PersistentSet[0]->org.abc.dvo.PrtnrGeoInfo["id"]->org.abc.dvo.PrtnrGeoInfoId["partner"]->
org.abc.dvo.Prtnr["prtnrGeoInfos"]->org.hibernate.collection.PersistentSet[0]->org.abc.dvo.PrtnrGeoInfo["id"]->org.abc.dvo.PrtnrGeoInfoId["partner"]->
org.abc.dvo.Prtnr["prtnrGeoInfos"]->org.hibernate.collection.PersistentSet[0]->org.abc.dvo.PrtnrGeoInfo["id"]->org.abc.dvo.PrtnrGeoInfoId["partner"]->
org.abc.dvo.Prtnr["prtnrGeoInfos"]->org.hibernate.collection.PersistentSet[0]->org.abc.dvo.PrtnrGeoInfo["id"]->org.abc.dvo.PrtnrGeoInfoId["partner"]->
...
at org.codehaus.jackson.map.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:164)
at org.codehaus.jackson.map.ser.BeanSerializer.serialize(BeanSerializer.java:112)
at org.codehaus.jackson.map.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:446)
at org.codehaus.jackson.map.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:150)
...
The Prtnr class is :
public class Prtnr implements Cloneable, java.io.Serializable {
private static final long serialVersionUID = 201207021420600052L;
private Integer prtnrId;
private String creatUserId;
private Date creatTs;
private String updtUserId;
private Date updtTs;
private String prtnrNm;
private Integer cncilNum;
private Character prtnrTypCd;
private Set<PrtnrGeoInfo> prtnrGeoInfos = new HashSet<PrtnrGeoInfo>(0);
private Set<PrtnrDtl> prtnrDtls = new HashSet<PrtnrDtl>(0);
private Set<SuplyDtl> suplyDtls = new HashSet<SuplyDtl>(0);
private Set<TrnsprtDtl> trnsprtDtls = new HashSet<TrnsprtDtl>(0);
private Set<PrtnrFacil> prtnrFacils = new HashSet<PrtnrFacil>(0);
private Set<PrtnrHumanResrc> prtnrHumanResrcs = new HashSet<PrtnrHumanResrc>(0);
.....
.....
Getters and setters for these properties
...
}
The PrtnrGeoInfo class is :
public class PrtnrGeoInfo implements java.io.Serializable {
private PrtnrGeoInfoId id = new PrtnrGeoInfoId();
private String creatUserId;
private Date creatTs;
private String updtUserId;
private Date updtTs;
Getters and setters for these properties
}
The PrtnrGeoInfoId class is :
public class PrtnrGeoInfoId implements java.io.Serializable {
private Prtnr partner;
private GeoSegment geoSegment;
private static final long serialVersionUID = 201207060857580050L;
Getters and setters for these properties
}
I believe it is because of the classes refrencing each other. But how can this problem be resolved. Within the app which is Struts 2 and Spring, this object get passed just fine.
The controller class is as follows:
#Controller
#RequestMapping("/partners")
public class PartnerController {
#RequestMapping(value="/{id}", method=RequestMethod.GET, headers ={"Accept=text/xml,application/json"})
#ResponseBody
public Prtnr getPartner(#PathVariable("id") String id) throws Exception{
Prtnr partner = null;
try{
partner = partnerService.getPartnerById(Integer.valueOf(id));
System.out.println("******* Test message " );
}catch(Exception ex){
System.out.println("******* Exception thrown ... " + ex.getMessage());
}
return partner;
}
}
The calling class is
public class TestTemplate
{
private static final long serialVersionUID = 1130201273334264152L;
public static void main(String[] args){
Prtnr partner = (Prtnr)new RestTemplate().getForObject("http://localhost:9080/respondersApp/testWs/partners/{id}", Prtnr.class, "1");
System.out.println("partner name is : " + partner.getPrtnrNm());
}
}
In this link you can find how to solve this.
However below I'll paste the solution in practice.
It's very simple. Assuming that your database query already works without JSON, all you have to do is this:
Add the #JsonManagedReference In the forward part of the relationship (i.e. User.java class):
#Entity
public class User implements java.io.Serializable{
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private long id;
#Column(name="name")
private String name;
#ManyToMany
#JoinTable(name="users_roles",joinColumns=#JoinColumn(name = "user_fk"),
inverseJoinColumns=#JoinColumn(name = "role_fk"))
#JsonManagedReference
private Set<Role> roles = new HashSet<Role>();
...
Add the #JsonBackReference In the back part of the relationship (i.e. Role.java class):
#Entity
public class Role implements java.io.Serializable {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
#ManyToMany(mappedBy="roles")
#JsonBackReference
private Set<User> users = new HashSet<User>();
...
The work is done. If you take a look at your firebug logs, you'll notice that the infinite recursive loop has disappeared.
This is quite a common scenario for me when you are trying to convert entity classes into JSON format. The simplest solution is just to use #JsonIgnore on the reverse mapping to break the cycle.
You can annotate the second reference of Prtnr in PrtnrGeoInfoId with #JsonBackReference
The infinite recursion is due to the following:
Class Prtnr contains Set<PrtnrGeoInfo> prtnrGeoInfos and each PrtnrGeoInfo contains PrtnrGeoInfoId id which in turn contains Prtnr partner.
Thus, Prtnr -> PrtnrGeoInfo ->PrtnrGeoInfoId ->Prtnr, is causing a cyclic dependency which is a problem for Jackson when it is trying to do the POJO Mapping.
You need to remove this cyclic dependency to fix this exception.