serializing object using gson and getting LazyInitializationException - java

This is my Controller method, i am trying to read my database by providing zip, cityname and province name.
#RequestMapping(value = "/retrieve", method = RequestMethod.GET)
public #ResponseBody String retrieveObjectThroughAjax(ModelMap model){
//Calling Service Method to read data according to zip,cityName and province provide
PropertyItems propertyItems=getPropertyTypeandAddressService.readAddressFromZip("H2H-
2N3","Montreal","Quebec");
Gson gson = new Gson();
String json = null;
try{
json = gson.toJson(propertyItems); // serializing object
}catch(Exception e){
logger.error(Constants.METHOD_INSIDE_MESSAGE +"getAuthors",e);
}
logger.debug(json);
return json;
}
}
Service Method
#Service
public class GetPropertyTypeandAddressServiceImpl implements GetPropertyTypeandAddressService{
#Autowired
private GetPropertyTypeandAddressDAO getPropertyTypeandAddressDAO;
#Transactional
public PropertyItems readAddressFromZip(String zipCode,String cityName,String provinceName){
PropertyItems propertyItems=getPropertyTypeandAddressDAO.getAddressFromZip(zipCode, cityName, provinceName);
Hibernate.initialize(propertyItems);
return propertyItems;
}
}
DAO Method
#Repository
public class GetPropertyTypeandAddressDAOimp implements GetPropertyTypeandAddressDAO{
#Autowired
private SessionFactory sessionFactory;
#Override
public PropertyItems getAddressFromZip(String zipCode,String cityName,String provinceName) {
PropertyItems propertyitems = new PropertyItems();
Criteria criteria = sessionFactory.getCurrentSession().createCriteria(PropertyItems.class,"propertyItemsClass");
if(zipCode != null){
criteria.createAlias("propertyItemsClass.address","address");
criteria.add(Restrictions.eq("address.zip",zipCode));
List<PropertyItems> propertyitem = criteria.list();
if(propertyitem.size()>0){
propertyitems = propertyitem.get(0);
}
}
else if(cityName != null){
criteria.createAlias("propertyItemsClass.address","address");
criteria.add(Restrictions.eq("address.city","city"));
criteria.add(Restrictions.eq("city.cityname",cityName));
List<PropertyItems> propertyitem = criteria.list();
if(propertyitem.size()>0){
propertyitems = propertyitem.get(0);
}
}
else if(provinceName != null){
criteria.createAlias("propertyItemsClass.address","address");
criteria.add(Restrictions.eq("address.city","city"));
criteria.add(Restrictions.eq("city.provinces","provinces"));
criteria.add(Restrictions.eq("provinces.provinceName",provinceName));
List<PropertyItems> propertyitem = criteria.list();
if(propertyitem.size()>0){
propertyitems = propertyitem.get(0);
}
}
return propertyitems;
}
}
Console Error
09:53:56,988 ERROR HelloController:567 - Inside Method: getAuthors org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.agilemaple.common.entity.Property.propertyType, no session or session was closed
at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:383)
at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:375)
As requested my Property Items Look like this
Entity:
Propert Items
#Entity
#Table(name="web_property_item")
public class PropertyItems {
#Id
#GeneratedValue
private Integer id;
private String name;
#ManyToOne
#JoinColumn(name="property_type_id")
private PropertyType propertyType;
#OneToOne(mappedBy="propertyItems",cascade=CascadeType.ALL)
private Address address;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public PropertyType getPropertyType() {
return propertyType;
}
public void setPropertyType(PropertyType propertyType) {
this.propertyType = propertyType;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
Entity : Property Type
#Entity
#Table(name="web_property_type")
public class PropertyType {
#Id
#GeneratedValue
private Integer id;
private String name;
#ManyToOne
#JoinColumn(name="property_id")
private Property property;
#OneToMany(fetch = FetchType.LAZY,mappedBy="propertyType", cascade=CascadeType.ALL)
private Set<PropertyItems> propertyItems;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Property getProperty() {
return property;
}
public void setProperty(Property property) {
this.property = property;
}
public Set<PropertyItems> getPropertyItems() {
return propertyItems;
}
public void setPropertyItems(Set<PropertyItems> propertyItems) {
this.propertyItems = propertyItems;
}
}

The problem in hibernate. Your field Set of properties has Lazy fetch method, it means that it will try to get when you call method get of this set. When u calling tojson methods, gson calls all get methods of object but in this moment hibernate session is close and hibernate can't open it in controller. I've faced with the same problem but directly on JSP. In a three weeks i resolved it by one more property for hibernate ( in your case) and I write code to opening session in view interceptor. I'm underground just right now, so I can't show property, but in a hour I will edit this answer and add property.
Added:
I remembered ! property is: hibernate.enable_lazy_load_no_trans = true
If it won't help, I will add code of opensessioninviewinterceptor.
#Override
public void addInterceptors(InterceptorRegistry registry) {
OpenSessionInViewInterceptor sessionInViewInterceptor = new OpenSessionInViewInterceptor();
sessionInViewInterceptor.setSessionFactory(sessionFactory());
}

Related

Complex Json to Nested POJO spring MVC

I am trying to get the following Json into POJOS using #RequestBody Instance instance
{
"service_id": "service-id-here",
"plan_id": "plan-id-here",
"context": {
"platform": "cloudfoundry",
"some_field": "some-contextual-data"
},
"organization_guid": "org-guid-here",
"space_guid": "space-guid-here",
"parameters": {
"agent_name": 1,
"url": "foo",
"credential": "asdasd",
"ia_url": "asdasd"
}
}
Below are my POJOs
Instance
public class Instance {
#JsonProperty(value = "service_id")
String serviceId;
#JsonProperty(value = "plan_id")
String planId;
//TODO : Replace with Context class when the spec defines things clearly
#JsonProperty(value = "context")
Object context;
#JsonProperty(value = "organization_guid")
String organizationGuid;
#JsonProperty(value = "space_guid")
String spaceGuid;
#JsonProperty(value = "parameters")
Parameters parameters;
}
Parameters
public class Parameters {
#JsonProperty(value = "agent_name")
String agentName;
#JsonProperty(value = "url")
String url;
#JsonProperty(value = "credential")
String credential;
#JsonProperty(value = "ia_url")
String iaUrl;
}
I use #JsonProperty everywhere. Is there any way to get underscore separated json keys into java's naming convention for variables (Camelcase)??
I tried using #JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) to my POJO classes instead of the #JsonProperty for each parameter. I just get an empty json {} in instance. What am I missing here?
Yes, is this possible using PropertyNamingStrategy class through JsonNaming annotation
Ex:
#JsonNaming(PropertyNamingStartergy.LowerCaseWithUnderscoresStrategy.class)
class Class_name{
...
}
//----
The below code has updated. In that code am using
PropertyNamingStrategy.SnakeCaseStrategy
Working code (TESTED).
Getters and setters are important for this to work. But #JsonProperty does not require them
User.java
#JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
public class User {
private int id;
private String beanName;
private Role role;
public Role getRole() {
return role;
}
public void setRole(Role role) {
this.role = role;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getBeanName() {
return beanName;
}
public void setBeanName(String beanName) {
this.beanName = beanName;
}
}
Role.java
#JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
public class Role {
private int id;
private String roleName;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
}
Here is the Controller
#RestController
#RequestMapping("/test")
public class NamingController {
#RequestMapping(value="/jsontopojo", method = RequestMethod.POST)
public ResponseEntity<User> jsontopojo(#RequestBody User nam) {
return new ResponseEntity<User>( nam, HttpStatus.OK);
}
}

How to select fields in spring data?

I have entity as follow.
#Entity
#Table(name = "BankProduct")
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
#ManyToOne
private ProductUseType type;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#ManyToOne
private ProductSerial serial;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public ProductUseType getType() {
return type;
}
public void setType(ProductUseType type) {
this.type = type;
}
public ProductSerial getSerial() {
return serial;
}
public void setSerial(ProductSerial serial) {
this.serial = serial;
}
}
My controller is :
#RestController
public class DEmoController {
#Autowired
private ProductRepository productRepository;
#GetMapping("/products")
public Returns products() {
return new Returns(ReturnStatus.SUCCESS.getStatus(), productRepository.findAll(), null);
}
}
It will load both of type and serial of product.
Can I only load type but not to load serial?
I don't want to add fetch=FetchType.LAZY to serial, because if next time I want to load serial but not to load type, it will be terrible.
Check the Projection interface
Create a interface ProductProjection
interface ProductProjection {
String getName();
String getType();
}
and add a method in you Repository
List<ProductProjection> findAllProjection()
That's the whole point of fetch=FetchType.LAZY. It'll not load any of your types/fields until you ask for them explicitly.
Take a look at this question: Link

Why the same id is incremented when I save an object in the repository?

Whenever I call save() method the same ID is shared between three different entities and I don't know why ?
#Entity
public class Department {
#Id
#GeneratedValue
private Long departmentId;
private String name;
public Department(Long departmentId) {
this.departmentId = departmentId;
}
public Department() {
}
public Department(String name) {
this.name = name;
}
public Long getDepartmentId() {
return departmentId;
}
public void setDepartmentId(Long departmentId) {
this.departmentId = departmentId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
#Entity
public class Location {
#Id
#GeneratedValue
private Long locationId;
private String name;
public Location(Long locationId) {
this.locationId = locationId;
}
public Location() {
}
public Location(String name) {
this.name = name;
}
public Long getLocationId() {
return locationId;
}
public void setLocationId(Long locationId) {
this.locationId = locationId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
And this is my Controller:
#RestController
public class SettingsController {
#Autowired
private LocationRepository locationRepository;
#Autowired
private DepartmentRepository departmentRepository;
#Autowired
private RoleRepository roleRepository;
#RequestMapping(value = "/api/locations", method = RequestMethod.POST)
public ResponseEntity addLocation(#RequestBody DataForm dataForm) {
if (dataForm == null) {
return new ResponseEntity(HttpStatus.BAD_REQUEST);
}
locationRepository.save(new Location(dataForm.getName()));
return new ResponseEntity(HttpStatus.CREATED);
}
#RequestMapping(value = "/api/roles", method = RequestMethod.POST)
public ResponseEntity addRole(#RequestBody DataForm dataForm) {
if (dataForm == null) {
return new ResponseEntity(HttpStatus.BAD_REQUEST);
}
roleRepository.save(new Role(dataForm.getName()));
return new ResponseEntity(HttpStatus.CREATED);
}
#RequestMapping(value = "/api/departments", method = RequestMethod.POST)
public ResponseEntity addDepartment(#RequestBody DataForm dataForm) {
if (dataForm == null) {
return new ResponseEntity(HttpStatus.BAD_REQUEST);
}
departmentRepository.save(new Department(dataForm.getName()));
return new ResponseEntity(HttpStatus.CREATED);
}
}
This should happen only if the id would be static, but It's not. If I create two new Location() objects, when I will create a new Department() the Id of the department will be 3. Why ?
Since you didn't specify the strategy for #GeneratedValue, I guess that Hibernate uses the same sequence for all your entities.
You can set something like
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="department_seq_gen")
#SequenceGenerator(name="department_seq_gen", sequenceName="DEPARTMENT_SEQ")
on Department entity, and something similar on Location entity (just use location_seq_gen and LOCATION_SEQ).

Controller and hibernate integration. Not saved entity

I have problem with JPA provided by Hibernate.
Here are my entities:
#Entity
public class Action {
#Id
#GeneratedValue
private Integer id;
#Column
private String name;
#JoinColumn
#ManyToOne
private User user;
public Integer getId() { return this.id; }
protected void setId(Integer id) { this.id = id; }
public String getName() { return this.name; }
public void setName(String name) { this.name = name; }
public void getUser() { return this.user; }
protected void setUser(User user) { this.user = user; }
}
#Entity
public class User {
#Id
#GeneratedValue
private Integer id;
#Column
private String name;
#OneTomany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<Action> actions = new ArrayList<>();
public Integer getId() { return this.id; }
protected void setId(Integer id) { this.id = id; }
public String getName() { return this.name; }
public void setName(String name) { this.name = name; }
public void addAction(Action action) {
action.setUser(this);
actions.add(action);
}
public void removeAction(Action action) {
actions.remove(action);
action.setUser(null);
}
public List<Action> getActions() { return Collections.unmodifiableList(actions); }
}
Here is my controller code:
#Controller
#RequestMapping("/user/{username}")
#SessionAttributes("user")
public class ActionController {
#Autowired
private TestService service;
#ModelAttribute("user")
public User contextRegistration(#PathVariable("username") String username) {
return this.service.getByName(username);
}
#RequestMapping("")
public String userView() {
return "user";
}
#RequestMapping(value = "/add", method = RequestMethod.GET)
public String addAction(#ModelAttribute Action action) {
return "addAction";
}
#RequestMapping(value = "/add", method = RequestMethod.POST)
public String postAddAction(#Valid Action action, BindingResult result, User user, SessionStatus sessionStatus) {
if(result.hasErrors()) {
return "addAction";
}
user.addAction(action);
this.service.saveAction(action);
sessionStatus.setComplete();
return "redirect:/user/{username}";
}
}
When method postAddAction is executed I got the following error:
org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : pl.zaprogramowany.test.model.Action.user -> pl.zaprogramowany.test.model.User
in line where method saveAction is executed.
My code is modeled on this code
https://github.com/spring-projects/spring-petclinic/blob/master/src/main/java/org/springframework/samples/petclinic/web/PetController.java#L79 but I don't know why I have this error because my code is pretty similar to this.
Can anybody help?
#Edit
I'm using spring Jpa repositories and code in service layer is very simple.
#Transactional(readyOnly = true)
public User getByName(String name) {
return this.userRepository.getByName(name);
}
#Transactional
public void saveAction(Action action) {
this.actionRepository.save(action);
}
This problem arises when you dont include cascade="all" (if using xml) or cascade=CascadeType.ALL (if using annotations) on your collection mapping.
Which in your case exists but at the user entity but not at the action entity
hence we need to see how are we saving the action entity on the basis of which we can decide if we need to add cascade on action entity or not
Your EntityManager is not aware of the User instance that is injected in your method. Then, when you try to save the Action instance with a reference to unaware User instace you get the transient object exception.
I don't know exactly how your User instance is injected but with that instance you should use entity manager to find it from the database. Doing this, your entity manager is aware of which user you are trying to use. Take a look to this post

Jackson exception during serialization

I've faced the problem, which is a little bit close to this issue, but when I've done all the steps, i still have such an exception:
org.codehaus.jackson.map.JsonMappingException: No serializer found for
class org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer and
no properties discovered to create BeanSerializer (to avoid exception,
disable SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS) ) (through
reference chain:
java.util.ArrayList[0]->com.myPackage.SomeEntity["mainEntity"]->com.myPackage.MainEntity["subentity1"]->com.myPackage.Subentity1_$$_javassist_8["handler"])
here is code with my entities:
#JsonAutoDetect
public class MainEntity {
private Subentity1 subentity1;
private Subentity2 subentity2;
#JsonProperty
public Subentity1 getSubentity1() {
return subentity1;
}
public void setSubentity1(Subentity1 subentity1) {
this.subentity1 = subentity1;
}
#JsonProperty
public Subentity2 getSubentity2() {
return subentity2;
}
public void setSubentity2(Subentity2 subentity2) {
this.subentity2 = subentity2;
}
}
#Entity
#Table(name = "subentity1")
#JsonAutoDetect
public class Subentity1 {
#Id
#Column(name = "subentity1_id")
#GeneratedValue
private Long id;
#Column(name = "name", length = 100)
private String name;
#JsonIgnore
#OneToMany(mappedBy = "subentity1")
private List<Subentity2> subentities2;
#JsonProperty
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#JsonProperty
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//here I didin't add #JsonProperty, cause it leads to cycling during serialization
public List<Subentity2> getSubentity2s() {
return subentity2s;
}
public void setSubentity2s(List<Subentity2> subentity2s) {
this.subentity2s = subentity2s;
}
}
#Entity
#Table(name = "subentity2")
#JsonAutoDetect
public class Subentity2 {
#Id
#Column(name = "subentity2_id")
#GeneratedValue
private Long id;
#Column(name = "name", length = 50)
private String name;
#ManyToOne
#JoinColumn(name = "subentity1_id")
private Subentity1 subentity1;
#JsonProperty
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#JsonProperty
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#JsonProperty
public Subentity1 getSubentity1() {
return subentity1;
}
public void setSubentity1(Subentity1 subentity1) {
this.subentity1 = subentity1;
}
here is code of my method for transformation:
private String toJSON(Object model) {
ObjectMapper mapper = new ObjectMapper();
String result = "";
try {
result = mapper.writeValueAsString(model);
} catch (JsonGenerationException e) {
LOG.error(e.getMessage(), e);
} catch (JsonMappingException e) {
LOG.error(e.getMessage(), e);
} catch (IOException e) {
LOG.error(e.getMessage(), e);
}
return result;
}
I'll very grateful for any help, pieces of advice or code :)
UPD
alsp, I forgot to add piece of code from my controller:
String result = "";
List<SomeEntity> entities = someEntityService.getAll();
Hibernate.initialize(entities);
for (SomeEntity someEntity : entities) {
Hibernate.initialize(someEntity.mainEntity());
Hibernate.initialize(someEntity.mainEntity().subentity1());
Hibernate.initialize(someEntity.mainEntity().subentity2());
}
result = this.toJSON(entities);
I can't ignore any fields, cause I need them
Basically some of your fields are wrapped into lazy hibernate proxies.
Call Hibernate.initialize(model) before serializing your object, it will load your lazy collections and references.
But I would not mix database and view models, this is a bad practice. Create set of classes for your restful model and convert database entities to them before serialization.
I created a Bean with plane fields(String, Boolean, Double, etc.), which are in my classes and made method for transformation
If you are using lazy loading add this
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})

Categories

Resources