1

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

What does @Proxy(lazy = false) do?

@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?

Andrey
  • 158
  • 2
  • 11

1 Answers1

1

You are returning an object via @Responsebody, userObject which you are returning is proxy object as you using is `LAZY' fetch. In controller layer there is no longer active transaction to fetch the properties of entity object.

You can either do EAGER fetching for of object or make declare child properties as Transient.

Rohit
  • 70
  • 4
  • Dear, Rohit, thank you for answer, but my object doesn't have any child collections as you can see in my code of User. That is not clear for me in my case. This answer was in the mentioned links upper, when Entity had games collection, but my entity simple pojo entity while without with any relations. No child data. – Andrey Nov 15 '17 at 11:38
  • Sorry I meant properties of the entity object. Since you are using LAZY fetching, any properties of entity expect ID would not be fetched. – Rohit Nov 15 '17 at 14:18
  • But as I know fetch type is applicable to the collection fields of my Entity. My Entity doesn't have any collection field. EAGER: it fetches the child entities along with parent. No child entities. Where should I set this property? – Andrey Nov 15 '17 at 15:18
  • The getOne methods returns only the reference from DB (lazy loading) . @Proxy(lazy=false) will disable the default lazy loading for a particular entity. That why may be your issue was getting resolved after use of this. You can try by using findOne method – Rohit Nov 16 '17 at 10:08
  • With method findOne works. As I understand this is prevent lazy loading. But I need this. I tried anther trick with HibernateAwareObjectMapper, but it doesn't work also with getOne. – Andrey Nov 17 '17 at 13:10