Now I am making project with REST API for the smart flat. I am using Spring, Spring, Hibernate, MySql. For now I have created entity for user and I faced with such exception when I try retrieve the user by my API:
Could not write JSON: could not initialize proxy - no Session;
I have found the solution how to prevent this exception by next links
JsonMappingException: could not initialize proxy - no Session
@Proxy(lazy = false) - has solved my problem, but I realy don't understand why session is closed in the spring controller when i try to get the user?
When I persist the user - no such exception! Nevertheless that the same service is used for persisting and getting objects. How can it be?
Logic of my application is started from web.xml. I am using
org.springframework.web.servlet.DispatcherServlet
This servlet maps requests and transfer to my controller. I am using spring json message converter
org.springframework.http.converter.json.MappingJackson2HttpMessageConverter
Configuration for spring's dispatcherServlet is
<mvc:annotation-driven/>
<context:component-scan base-package="com.smartcore.controllers.mysql"/>
<!-- Configure to plugin JSON as request and response in method handler -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<ref bean="jsonMessageConverter"/>
</list>
</property>
</bean>
<!-- Configure bean to convert JSON to POJO and vice versa -->
<bean id="jsonMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
</bean>
My Entity is simple and doesn't have any relations with othe entities
@Entity
@Table(name = "User")
//@Proxy(lazy = false)
public class UserMySql {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "user_id")
private Long id;
@Column(name = "name")
private String name;
@Column(name = "surname")
private String surname;
@Column(name = "email")
private String eMail;
@Column(name = "login")
private String login;
@Column(name = "password")
private String password;
public UserMySql() {}
public UserMySql(String name, String surname, String login, String password, String eMail) {
this.name = name;
this.surname = surname;
this.login = login;
this.password = password;
this.eMail = eMail;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSurname() {
return surname;
}
public void setSurname(String surname) {
this.surname = surname;
}
public String getLogin() {
return login;
}
public void setLogin(String login) {
this.login = login;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String geteMail() {
return eMail;
}
public void seteMail(String eMail) {
this.eMail = eMail;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("UserMySql [id=");
sb.append(id);
sb.append(", name=");
sb.append(name);
sb.append(", surname=");
sb.append(surname);
sb.append(", eMail=");
sb.append(eMail);
sb.append(", login=");
sb.append(login);
sb.append(", password=");
sb.append(password);
sb.append("]");
return sb.toString();
}
Code for Controller
@RestController
public class MySqlUserAccountController {
private UserMySqlService userMySqlService;
@Autowired
public MySqlUserAccountController( UserMySqlService userMySqlService) {
this.userMySqlService = userMySqlService;
};
@GetMapping("/user/{userId}")
public @ResponseBody UserMySql findUserById(@PathVariable("userId") Long userId) {
return userMySqlService.getUserById(userId);
}
@PostMapping("/user/add")
public void addUser(@RequestBody UserMySql user){
userMySqlService.save(user);
}
}
I am using Spring data and it was used native methot of JpaRepository getOne(id) to retrieve user.
return this.userRepo.getOne(id);
Also I have found the solution to use
org.springframework.orm.hibernate5.support.OpenSessionInViewFilter
But configuring filter and adding bean session has not helped.
web.xml added
<servlet-mapping>
<servlet-name>dispServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<filter>
<filter-name>OpenSessionInViewFilter</filter-name>
<filter-class>org.springframework.orm.hibernate5.support.OpenSessionInViewFilter</filter-class>
<!-- Replace with hibernate3, hibernate4 or hibernate5 depending on the
hibernate version one uses -->
</filter>
application-context.xml added
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="packagesToScan" value="com.smartcore.entities.mysqldb"/>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
</props>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
What is best way for implementation? and if use @Proxy(lazy = false)- that lead to live link to my object from any object contains it, can it lead to the increasing memory consumption?
In previous answers I have not found reason why session was invalidated in the controller. Could you please describe in more details what is trhe reason?
Update
I still has not solved problem with lazy loading. I found solution described here
http://blog.pastelstudios.com/2012/03/12/spring-3-1-hibernate-4-jackson-module-hibernate/
But in my case it doesnt't work and I don't understand why ?
web.xml config
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<!-- Processes application requests -->
<servlet>
<servlet-name>dispServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/dispatcher-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- needed for ContextLoaderListener -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>WEB-INF/spring/root-context.xml</param-value>
</context-param>
<!-- Bootstraps the root web application context before servlet initialization -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
dispatcher-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<context:component-scan base-package="com.smartcore.controllers.mysql" />
<mvc:annotation-driven>
<mvc:message-converters>
<bean
class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper">
<bean class="com.smartcore.mappers.json.HibernateAwareObjectMapper" />
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
</beans>
HibernateAwareObject
public class HibernateAwareObjectMapper extends ObjectMapper {
private static final long serialVersionUID = 5371945173448137189L;
public HibernateAwareObjectMapper() {
this.registerModule(new Hibernate5Module());
}
}
In the log I don't see that my custom object will be called. It looks like something with configuration.
2017-11-17 13:58:50 DEBUG DispatcherServlet:891 - DispatcherServlet with name 'dispServlet' processing GET request for [/Smart_Core_Control/user/3] 2017-11-17 13:58:50 DEBUG RequestMappingHandlerMapping:313 - Looking up handler method for path /user/3 2017-11-17 13:58:50 DEBUG RequestMappingHandlerMapping:320 - Returning handler method [public com.smartcore.entities.mysqldb.UserMySql com.smartcore.controllers.mysql.MySqlUserAccountController.findUserById(java.lang.Long)] 2017-11-17 13:58:50 DEBUG DefaultListableBeanFactory:255 - Returning cached instance of singleton bean 'mySqlUserAccountController' 2017-11-17 13:58:50 DEBUG DispatcherServlet:979 - Last-Modified value for [/Smart_Core_Control/user/3] is: -1 2017-11-17 13:58:50 DEBUG TransactionalRepositoryProxyPostProcessor$CustomAnnotationTransactionAttributeSource:354 - Adding transactional method 'getOne' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly; '' 2017-11-17 13:58:50 DEBUG DefaultListableBeanFactory:255 - Returning cached instance of singleton bean 'transactionManager' 2017-11-17 13:58:50 DEBUG JpaTransactionManager:368 - Creating new transaction with name [org.springframework.data.jpa.repository.support.SimpleJpaRepository.getOne]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly; '' 2017-11-17 13:58:50 DEBUG JpaTransactionManager:391 - Opened new EntityManager [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=ExecutableList{size=0} updates=ExecutableList{size=0} deletions=ExecutableList{size=0} orphanRemovals=ExecutableList{size=0} collectionCreations=ExecutableList{size=0} collectionRemovals=ExecutableList{size=0} collectionUpdates=ExecutableList{size=0} collectionQueuedOps=ExecutableList{size=0} unresolvedInsertDependencies=null])] for JPA transaction 2017-11-17 13:58:50 DEBUG DriverManagerDataSource:143 - Creating new JDBC DriverManager Connection to [jdbc:mysql://localhost:3306/core_data] Fri Nov 17 13:58:50 EET 2017 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification. 2017-11-17 13:58:50 DEBUG DataSourceUtils:176 - Setting JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@29154676] read-only 2017-11-17 13:58:50 DEBUG TransactionImpl:55 - begin 2017-11-17 13:58:50 DEBUG JpaTransactionManager:423 - Exposing JPA transaction as JDBC transaction [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@34dcedf7] 2017-11-17 13:58:50 DEBUG JpaTransactionManager:739 - Initiating transaction commit 2017-11-17 13:58:50 DEBUG JpaTransactionManager:531 - Committing JPA transaction on EntityManager [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=ExecutableList{size=0} updates=ExecutableList{size=0} deletions=ExecutableList{size=0} orphanRemovals=ExecutableList{size=0} collectionCreations=ExecutableList{size=0} collectionRemovals=ExecutableList{size=0} collectionUpdates=ExecutableList{size=0} collectionQueuedOps=ExecutableList{size=0} unresolvedInsertDependencies=null])] 2017-11-17 13:58:50 DEBUG TransactionImpl:66 - committing 2017-11-17 13:58:50 DEBUG DataSourceUtils:233 - Resetting read-only flag of JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@29154676] 2017-11-17 13:58:50 DEBUG JpaTransactionManager:622 - Closing JPA EntityManager [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=ExecutableList{size=0} updates=ExecutableList{size=0} deletions=ExecutableList{size=0} orphanRemovals=ExecutableList{size=0} collectionCreations=ExecutableList{size=0} collectionRemovals=ExecutableList{size=0} collectionUpdates=ExecutableList{size=0} collectionQueuedOps=ExecutableList{size=0} unresolvedInsertDependencies=null])] after transaction 2017-11-17 13:58:50 DEBUG EntityManagerFactoryUtils:419 - Closing JPA EntityManager 2017-11-17 13:58:50 DEBUG ExceptionHandlerExceptionResolver:137 - Resolving exception from handler [public com.smartcore.entities.mysqldb.UserMySql com.smartcore.controllers.mysql.MySqlUserAccountController.findUserById(java.lang.Long)]: org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: could not initialize proxy - no Session; nested exception is com.fasterxml.jackson.databind.JsonMappingException: could not initialize proxy - no Session 2017-11-17 13:58:50 DEBUG ResponseStatusExceptionResolver:137 - Resolving exception from handler [public com.smartcore.entities.mysqldb.UserMySql com.smartcore.controllers.mysql.MySqlUserAccountController.findUserById(java.lang.Long)]: org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: could not initialize proxy - no Session; nested exception is com.fasterxml.jackson.databind.JsonMappingException: could not initialize proxy - no Session 2017-11-17 13:58:50 DEBUG DefaultHandlerExceptionResolver:137 - Resolving exception from handler [public com.smartcore.entities.mysqldb.UserMySql com.smartcore.controllers.mysql.MySqlUserAccountController.findUserById(java.lang.Long)]: org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: could not initialize proxy - no Session; nested exception is com.fasterxml.jackson.databind.JsonMappingException: could not initialize proxy - no Session 2017-11-17 13:58:50 WARN DefaultHandlerExceptionResolver:380 - Failed to write HTTP message: org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: could not initialize proxy - no Session; nested exception is com.fasterxml.jackson.databind.JsonMappingException: could not initialize proxy - no Session 2017-11-17 13:58:50 DEBUG DispatcherServlet:1076 - Null ModelAndView returned to DispatcherServlet with name 'dispServlet': assuming HandlerAdapter completed request handling 2017-11-17 13:58:50 DEBUG DispatcherServlet:1004 - Successfully completed request
What can be wrong?