JsonIgnore and JsonBackReference are being Ignored - json

i'm using spring 4.0.1 , hibernate 4.3.5 ,jackson 1.9.2 and STS IDE
I'm creating a RESTful webservice that returns a data in JSON format
when i use Hibernate code generator it generates getters and setter of associated entities annotated by #OneToMany(fetch = FetchType.LAZY, mappedBy = "user") for the source
and #ManyToOne(fetch = FetchType.LAZY) for the reference
which causes an infinite recursion during serialization. I tried using Jackson's #JsonIgnore and #JsonBackReference annotations to fix the problem but it seems as if they are being totally ignored and the infinite recursion is still occurring.
Caused by: com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion (StackOverflowError)
This is my entity classes
User.class
//i get that suggestion from some sites
#JsonIgnoreProperties({ "hibernateLazyInitializer", "handler" })
#Entity
#Table(name = "user", catalog = "someSchema")
public class User implements java.io.Serializable {
private String name;
private String password;
private String username;
private Set<Telephone> telephones = new HashSet<Telephone>(0);
#JsonManagedReference
#OneToMany(fetch = FetchType.LAZY, mappedBy = "user")
public Set<Telephone> getTelephones() {
return this.telephones;
}
public void setTelephones(Set<Telephone> telephones) {
this.telephones = telephones;
}
}
Telephone.class
#Entity
#Table(name = "telephone", catalog = "someSchema")
public class Telephone implements java.io.Serializable {
private User user;
private String telephone;
#ManyToOne(fetch = FetchType.LAZY)
//tried #JsonIgnore only and both
#JsonIgnore
//tried #JsonBackReference only and both
#JsonBackReference
#JoinColumn(name = "user_id", nullable = false)
public User getUser() {
return this.user;
}
#JsonIgnore
#JsonBackReference
public void setUser(User user) {
this.user = user;
}
}
concerning registering jackson to my application, i used xml config
<mvc:annotation-driven>
<mvc:message-converters>
<bean
class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper">
<bean
class="web.jsonConverters.HibernateAwareObjectMapper" />
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
and mapper class
public class HibernateAwareObjectMapper extends ObjectMapper {
public HibernateAwareObjectMapper() {
Hibernate4Module hm = new Hibernate4Module();
registerModule(hm);
}
}
Do you have any idea why the Jackson annotations are being ignored?
any help will be appreciated...

use
import com.fasterxml.jackson.annotation.JsonBackReference;
instead of
import org.codehaus.jackson.annotate.JsonBackReference;

i found a way by annotating the setter by #Transient
idont know why but it works fine
User.class
//i get that suggestion from some sites
#JsonIgnoreProperties({ "hibernateLazyInitializer", "handler" })
#Entity
#Table(name = "user", catalog = "someSchema")
public class User implements java.io.Serializable {
private String name;
private String password;
private String username;
private Set<Telephone> telephones = new HashSet<Telephone>(0);
#OneToMany(fetch = FetchType.LAZY, mappedBy = "user")
public Set<Telephone> getTelephones() {
return this.telephones;
}
public void setTelephones(Set<Telephone> telephones) {
this.telephones = telephones;
}
}
Telephone.class
#Entity
#Table(name = "telephone", catalog = "someSchema")
public class Telephone implements java.io.Serializable {
private User user;
private String telephone;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "user_id", nullable = false)
public User getUser() {
return this.user;
}
#Transient
public void setUser(User user) {
this.user = user;
}
}
Another Naive Solution
I solved it manually in my RESTful Controller
by loop over the set of telephones and set user to null
#RestController
#RequestMapping("/user")
public class UserController extends ParentController {
static final Logger logger = Logger.getLogger(UserController.class.getName());
#Autowired
IUserDao iuserdao;
#RequestMapping(value = "/signin", method = RequestMethod.POST)
public ResponseEntity<User> signin(#RequestBody LoginWrapper login) {
System.out.println("==============GET USER==============");
try {
User user = iuserdao.signin(login);
if (user == null) {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.set(ERR_HEADER_NAME, "user not exist");
return new ResponseEntity<User>(httpHeaders, HttpStatus.NOT_FOUND);
} else {
List<Telephone> tels=user.getTelephones();
for (Telephone telephone : tels) {
telephone.setUser(null);
}
return new ResponseEntity<User>(user, HttpStatus.OK);
}
} catch (Exception e) {
System.out.println(e.getMessage());
return null;
}
}
still need a better answer concerning Jackson problem..

Related

How to save a raw JSON in the MySQL using JPA

I need to save this JSON in MySQL database, and later recover it and display in the frontend
I'm using Spring boot with JPA, but i have been facing errors about serialization when the Request comes from frontend, like this:
I send the json from the frontend to my controller:
#RestController
#RequestMapping("/theoriclesson")
#CrossOrigin
public class TheoricLessonController implements ITheoricLessonController {
#Autowired
private TheoricLessonRepository theoricLessonRepository;
#Autowired
private ModuleRepository moduleRepository;
#Override
#PostMapping("/")
public void add(#RequestBody TheoricLesson theoricLesson) {
Module module = new Module();
module.setName("Modulo de teste");
TheoricLesson tc = new TheoricLesson();
Module mP = moduleRepository.save(module);
tc.setModule(mP);
theoricLessonRepository.save(tc);
}
}
and my entities:
#Data
#Entity
#Builder
public class TheoricLesson {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Long xpEarned;
private String lessonName;
#Embedded
private DraftJsText draftJsText;
#ManyToOne
#JoinColumn(name = "module_id")
#JsonIgnore
private Module module;
}
#Getter
#Setter
#ToString
#Embeddable
public class DraftJsText {
public DraftJsText() {
}
public DraftJsText(String blocks, String entityMap) {
this.blocks = blocks;
this.entityMap = entityMap;
}
#Column(columnDefinition = "JSON")
private String blocks;
#Column(columnDefinition = "JSON")
private String entityMap;
}

how to use spring data jpa repository( jackson ) to deserialize posted json which has nested data?

I use spring data jpa and rest to expose restful api, actually spring-boot-starter-data-jpa and spring-boot-starter-data-rest.
My Entity definations are:
User
#Entity
#Table(name="users")
#JsonDeserialize(using = User.Deserialize.class)
public class User implements Serializable {
private static final long serialVersionUID = 7802610584713776262L;
#Id
#Column(name = "username")
private String userName;
#ManyToMany(
cascade = {CascadeType.REFRESH},
fetch = FetchType.EAGER
)
#JoinTable(
name = "user_roles",
joinColumns = #JoinColumn(name = "username"),
inverseJoinColumns = #JoinColumn(name = "role")
)
#Fetch(FetchMode.JOIN)
private Set<Role> roles = new HashSet<>();
#ManyToMany(
cascade = {CascadeType.REFRESH},
fetch = FetchType.EAGER
)
#JoinTable(
name = "user_permissions",
joinColumns = #JoinColumn(name = "username"),
inverseJoinColumns = #JoinColumn(name = "permission")
)
#Fetch(FetchMode.JOIN)
private Set<Permission> permissions = new HashSet<>();
// ... getters and setters ...
public static class Deserialize extends JsonDeserializer<User> {
#Override
public User deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
String txt = jsonParser.readValueAsTree().toString();
return JSON.parseObject(txt, User.class);
}
}
}
role
#Entity
#Table(name="roles")
#JsonDeserialize(using = Role.Deserialize.class)
public class Role implements Serializable {
private static final long serialVersionUID = -53314161963168175L;
#Id
#Column(name="role_name")
private String roleName;
private String info;
#ManyToMany(
targetEntity=Permission.class,
cascade={CascadeType.REFRESH},
fetch=FetchType.EAGER
)
#JoinTable(
name="roles_permissions",
joinColumns=#JoinColumn(name="role_name"),
inverseJoinColumns=#JoinColumn(name="permission")
)
#Fetch(FetchMode.SELECT)
private Set<Permission> permissions = new HashSet<>();
public static class Deserialize extends JsonDeserializer<Role> {
#Override
public Role deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
String txt = jsonParser.readValueAsTree().toString();
return JSON.parseObject(txt, Role.class);
}
}
}
permission
#Entity
#Table(name="permissions")
#JsonDeserialize(using = Permission.Deserialize.class)
public class Permission implements Serializable {
private static final long serialVersionUID = -3716946708377038760L;
#Id
private String permission;
private String info;
#ManyToOne(
cascade={CascadeType.REFRESH},
fetch=FetchType.EAGER
)
#JoinColumn(name="parent")
#Fetch(FetchMode.JOIN)
private Permission parent;
public static class Deserialize extends JsonDeserializer<Permission> {
#Override
public Permission deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
String txt = jsonParser.readValueAsTree().toString();
return JSON.parseObject(txt, Permission.class);
}
}
}
I managed these data from web, so i follows the step:
post permissions to server var host:port/permissions
post roles var host:port/roles
post permissions of roles var host:port/roles/role_id/permissions
post users var host:port/users
post roles of users var host:port/users/user_id/roles
post permission of users var host:port/users/user_id/permissions.
But i want to combine the step 2,3 to one single operation and 4,5,6 to another single operation by posting nested data.
For example:
assume perm1, perm2, perm3 has been created.
{roleName: "admin", permissions: ["perm1", "perm2"]}
{userName: "peter", roles: ["admin"], permissions: ["perm3"]}
I found a solution which i used in my code: the #JsonDeserializer. I defined deserializer for these entities, and used Fastjson to deserialize the nested data, which works.
Any has better solutions? I feel it can be done in jackson or spring, but i don't know. Any body help me?

spring rest lazy loading with hibernate

I am trying to develop spring rest api with hibernate.
after searching in google, I have not find solution to lazy loading.
I have two entity like below:
University.java
#Entity()
#Table(schema = "core", name = "university")
public class University extends BaseEntity {
private String uniName;
private String uniTelephon;
#LazyCollection(LazyCollectionOption.FALSE)
#OneToMany(fetch = FetchType.LAZY, mappedBy = "university", cascade = CascadeType.ALL)
#JsonManagedReference
private List<Student> students;
//setter and getter
}
Student.java
#Entity
#Table(schema = "core",name = "student")
public class Student {
#Id
#GeneratedValue
private long id;
private String firstName;
private String lastName;
private String section;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name = "UNIVERSITY_ID",nullable = false)
#JsonBackReference
private University university;
// setter and getter
}
any my rest end point
#GetMapping("/list")
public ResponseEntity list() throws Exception {
// I need to return just Universities But it return it eagerly with their students
return new ResponseEntity(this.universityService.findAll(), HttpStatus.OK);
}
after calling the rest api, it return university with all students.
There is a way to tell Jackson to not serialize the unfetched objects or collections?
Can somebody help me with a proved solution?
Try adding the following dependancy (depending on your hibernate version):
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-hibernate5</artifactId>
<version>${jackson.version}</version>
</dependency>
And then (assuming you have a Java based configuration) add the following in the WebMvcConfigurerAdapter class:
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(jackson2HttpMessageConverter());
}
#Bean
public MappingJackson2HttpMessageConverter jackson2HttpMessageConverter() {
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setObjectMapper(this.jacksonBuilder().build());
return converter;
}
public Jackson2ObjectMapperBuilder jacksonBuilder() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
Hibernate5Module hibernateModule = new Hibernate5Module();
hibernateModule.configure(Feature.FORCE_LAZY_LOADING, false);
builder.modules(hibernateModule);
// Spring MVC default Objectmapper configuration
builder.featuresToDisable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
builder.featuresToDisable(MapperFeature.DEFAULT_VIEW_INCLUSION);
return builder;
}
It should force the Jackson's objectMapper to not fetch lazy-loaded values.

Orika Bean Mapper Spring Data ManyToMany join table overriding

In my Spring project I have domain and model layer separately. Domain layer are just entities, that maps to tables in MySQL db. Models are used in service layer. I have two tables: User and Roles, that have ManyToMany relationship.
#Entity
#Table(name = "user")
public class UserEntity {
#Id
#GeneratedValue
private Long id;
private String name;
#ManyToMany(mappedBy="users", cascade=CascadeType.ALL,fetch=FetchType.EAGER)
private List<RoleEntity> roles;
//..Getters Setters
}
#Entity
#Table(name = "role")
public class RoleEntity {
#Id
#GeneratedValue
private Long id;
private String value;
#ManyToMany(cascade=CascadeType.ALL,fetch=FetchType.EAGER)
#JoinTable(name = "user_role",
joinColumns = #JoinColumn(name = "role_id") , inverseJoinColumns = #JoinColumn(name = "user_id") )
private List<UserEntity> users;
}
Model
public class User {
private Long id;
private String name;
private List<Role> roles;
}
public class Role {
private Long id;
private String value;
private List<User> users;
}
Repository Layer
#Repository
public interface UserRepo extends JpaRepository<UserEntity, Long>{
}
Service
#Service
public class UserService {
#Autowired
private UserRepo userRepo;
MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
MapperFacade mapper = mapperFactory.getMapperFacade();
public void save(User user) {
userRepo.save(mapper.map(user, UserEntity.class));
}
public User getUser(Long id) {
return mapper.map(userRepo.findOne(id), User.class);
}
}
The problem is - each time I retrieve user, and update it properties (name for example), and than save back it into table, row in the join table user_role, overrides too. For example if it was
id=1, user_id=1, role_id=1
, than after update it becomes
id=2, user_id=1, role_id=1
.
#Test
public void contextLoads() {
User user = userService.getUser(1L);
user.setName("kyk");
userService.save(user);
}
It works correct without mapper, so mapper is the reason. And I can't get any workaround. Has any one faced the same problem? Will be thankful for any help.

com.fasterxml.jackson.databind.JsonMappingException: object is not an instance of declaring class

I have a CookingEvent.class which is a subclass of Event.class and hibernate inheritance strategy is #Inheritance(strategy=InheritanceType.JOINED) . when i am trying to send a List objects as a get response .I am getting below Exception
2016-08-25 11:49:22.351 ERROR 11944 --- [nio-8189-exec-1]
o.a.c.c.C.[.[.[/].[servletContainer] : Servlet.service() for
servlet [servletContainer] in context with path [] threw exception
[org.glassfish.jersey.server.ContainerException:
com.fasterxml.jackson.databind.JsonMappingException: object is not an
instance of declaring class (through reference chain:
java.util.ArrayList[0]->Object[]["eventId"])] with root cause
java.lang.IllegalArgumentException: object is not an instance of
declaring class at
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497) at
com.fasterxml.jackson.databind.ser.BeanPropertyWriter.get(BeanPropertyWriter.java:726)
at
com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:506)
at
com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:644)
at
com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:152)
#Entity
#Table(name = "users")
#JsonAutoDetect
public class Users implements java.io.Serializable {
#JsonBackReference
private List<Event> eventByUser = new ArrayList<Event>(
0);
#Id
#GeneratedValue
#Column(name = "USER_ID", unique = true)
public Long getUserId() {
return userIds;
}
#OneToMany(fetch = FetchType.LAZY, mappedBy = "user")
#Fetch(FetchMode.JOIN)
#JsonInclude(JsonInclude.Include.NON_EMPTY)
#JsonIgnore
public List<Event> getEventByUser() {
return eventByUser;
}
}
#Entity
#Table(name = "EVENT")
#Inheritance(strategy=InheritanceType.JOINED)
public class Event implements java.io.Serializable {
private Integer eventId;
#Id
#GeneratedValue
#Column(name = "COOKING_EVENT_ID", unique = true)
public Integer getEventId() {
return eventId;
}
private Users user;
#ManyToOne(fetch = FetchType.LAZY)
#Fetch(FetchMode.SELECT)
#JsonIgnore
#JsonInclude(JsonInclude.Include.NON_EMPTY)
#JoinColumn(name = "ASSIGNED_USER_ID", nullable = false)
#JsonManagedReference
public Users getUser() {
return user;
}
public void setEventId(Integer eventId) {
this.eventId = eventId;
}
#Entity
#Table(name = "COOKING_REQUEST_EVENT") #PrimaryKeyJoinColumn(name="EVENT_ID")
public class CookingRequestEvent extends Event implements java.io.Serializable {
//Other Variables along with setters and getters
}
I have a Jersey controller as below which returns a List
#GET
#Path("/cookingEventsByUser/{userId}")
#Produces({ MediaType.APPLICATION_JSON})
public List<CookingEvent> getEventsById(#PathParam("userId") Long id)
throws JsonGenerationException, JsonMappingException, IOException {
List<CookingEvent> events = new ArrayList<CookingEvent>();
events = cookingEventServices.getCookingEventsByUser(id);
}
I am using Spring Boot + Jersey + hibernate
Issue is resolved .
Added
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include =As.PROPERTY, property = "data")
#JsonSubTypes({ #Type(value = CookingEvent.class, name = "cookingEvent"), #Type(value = CookingRequestEvent.class, name = "cookingRequest") })