为什么Spring控制器中的会话无效?使用spring MVC进行REST实现的最佳方法是什么?

时间:2017-11-15 08:45:37

标签: json spring hibernate

现在我正在使用REST API为智能平板制作项目。我正在使用Spring,Spring,Hibernate,MySql。 现在我已经为用户创建了实体,当我尝试通过我的API检索用户时,我遇到了这样的异常:

  

无法编写JSON:无法初始化代理 - 无会话;

我找到了解决方案如何通过下一个链接阻止此异常

  

JsonMappingException: could not initialize proxy - no Session

     

What does @Proxy(lazy = false) do?

@Proxy(lazy = false) - 解决了我的问题,但我真的不明白为什么当我试图让用户在Spring控制器中关闭会话时?

当我坚持用户时 - 没有这样的例外!然而,相同的服务用于持久化和获取对象。怎么会这样?

我的应用程序的逻辑是从web.xml开始的。我正在使用

  

org.springframework.web.servlet.DispatcherServlet

此servlet映射请求并传输到我的控制器。我正在使用spring json消息转换器

  

org.springframework.http.converter.json.MappingJackson2HttpMessageConverter

spring的dispatcherServlet的配置是

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

我的实体很简单,与其他实体没有任何关系

@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();
    }

控制器代码

@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);
    }

}

我正在使用Spring数据,它使用了JpaRepository getOne(id)的原生methot来检索用户。

return this.userRepo.getOne(id);

我也找到了使用

的解决方案
  

org.springframework.orm.hibernate5.support.OpenSessionInViewFilter

但是配置过滤器和添加bean会话没有帮助。

添加了web.xml

<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

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

实施的最佳方式是什么?如果使用@Proxy(lazy = false) - 导致从任何对象的实时链接包含它,它是否会导致内存消耗增加?

在之前的回答中,我没有找到控制器中会话无效的原因。你能否详细描述一下原因是什么?

更新

我仍然没有解决延迟加载的问题。我找到了这里描述的解决方案

  

http://blog.pastelstudios.com/2012/03/12/spring-3-1-hibernate-4-jackson-module-hibernate/

但在我的情况下,它不起作用,我不明白为什么?

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());

    }

}

在日志中,我看不到我的自定义对象会被调用。它看起来像配置的东西。

  

2017-11-17 13:58:50 DEBUG DispatcherServlet:891 - DispatcherServlet   名字&#39; dispServlet&#39;处理GET请求   [/ Smart_Core_Control / user / 3] 2017-11-17 13:58:50 DEBUG   RequestMappingHandlerMapping:313 - 查找路径的处理程序方法   / user / 3 2017-11-17 13:58:50 DEBUG RequestMappingHandlerMapping:320 -   返回处理程序方法[public   com.smartcore.entities.mysqldb.UserMySql   com.smartcore.controllers.mysql.MySqlUserAccountController.findUserById(java.lang.Long中)]   2017-11-17 13:58:50 DEBUG DefaultListableBeanFactory:255 - 返回   单例bean的缓存实例&#39; mySqlUserAccountController&#39;   2017-11-17 13:58:50 DEBUG DispatcherServlet:979 - Last-Modified值   for [/ Smart_Core_Control / user / 3]是:-1 2017-11-17 13:58:50 DEBUG   TransactionalRepositoryProxyPostProcessor $ CustomAnnotationTransactionAttributeSource:354    - 添加交易方法&#39; getOne&#39; with属性:PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly; &#39;&#39; 2017年11月17日   13:58:50 DEBUG DefaultListableBeanFactory:255 - 返回缓存   单例bean的实例&#39; transactionManager&#39; 2017-11-17 13:58:50   DEBUG JpaTransactionManager:368 - 使用名称创建新事务   [org.springframework.data.jpa.repository.support.SimpleJpaRepository.getOne]:   PROPAGATION_REQUIRED,ISOLATION_DEFAULT,只读; &#39;&#39; 2017年11月17日   13:58:50 DEBUG JpaTransactionManager:391 - 打开新的EntityManager   [SessionImpl(PersistenceContext [entityKeys = [],collectionKeys = []]; ActionQueue [插入= ExecutableList {大小= 0}   updates = ExecutableList {size = 0} deletions = ExecutableList {size = 0}   orphanRemovals = ExecutableList {大小= 0}   collectionCreations = ExecutableList {大小= 0}   collectionRemovals = ExecutableList {大小= 0}   collectionUpdates = ExecutableList {大小= 0}   collectionQueuedOps = ExecutableList {大小= 0}   对于JPA事务2017-11-17,unresolvedInsertDependencies = null])]   13:58:50 DEBUG DriverManagerDataSource:143 - 创建新的JDBC   DriverManager连接到[jdbc:mysql:// localhost:3306 / core_data]   Fri Nov 17 13:58:50 EET 2017 WARN:在没有的情况下建立SSL连接   不建议使用服务器的身份验证。根据MySQL   5.5.45 +,5.6.26 +和5.7.6+要求如果未设置显式选项,则必须默认建立SSL连接。为了合规   使用SSL的现有应用程序验证verifyServerCertificate   属性设置为“错误”。您需要显式禁用SSL   通过设置useSSL = false,或设置useSSL = true并为其提供truststore   服务器证书验证。 2017-11-17 13:58:50 DEBUG   DataSourceUtils:176 - 设置JDBC连接   只读2017-11-17只读[com.mysql.cj.jdbc.ConnectionImpl@29154676]   13:58:50 DEBUG TransactionImpl:55 - 2017-11-17 13:58:50开始调试   JpaTransactionManager:423 - 将JPA事务公开为JDBC   交易   [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@34dcedf7]   2017-11-17 13:58:50 DEBUG JpaTransactionManager:739 - 启动   交易提交2017-11-17 13:58:50 DEBUG JpaTransactionManager:531    - 在EntityManager上提交JPA事务[SessionImpl(PersistenceContext [entityKeys = [],collectionKeys = []]; ActionQueue [insertions = ExecutableList {size = 0}   updates = ExecutableList {size = 0} deletions = ExecutableList {size = 0}   orphanRemovals = ExecutableList {大小= 0}   collectionCreations = ExecutableList {大小= 0}   collectionRemovals = ExecutableList {大小= 0}   collectionUpdates = ExecutableList {大小= 0}   collectionQueuedOps = ExecutableList {大小= 0}   unresolvedInsertDependencies = null])] 2017-11-17 13:58:50 DEBUG   TransactionImpl:66 - 承诺2017-11-17 13:58:50 DEBUG   DataSourceUtils:233 - 重置JDBC连接的只读标志   [com.mysql.cj.jdbc.ConnectionImpl@29154676] 2017-11-17 13:58:50调试   JpaTransactionManager:622 - 关闭JPA EntityManager   [SessionImpl(PersistenceContext [entityKeys = [],collectionKeys = []]; ActionQueue [插入= ExecutableList {大小= 0}   updates = ExecutableList {size = 0} deletions = ExecutableList {size = 0}   orphanRemovals = ExecutableList {大小= 0}   collectionCreations = ExecutableList {大小= 0}   collectionRemovals = ExecutableList {大小= 0}   collectionUpdates = ExecutableList {大小= 0}   collectionQueuedOps = ExecutableList {大小= 0}   交易2017-11-17之后的unresolvedInsertDependencies = null])]   13:58:50 DEBUG EntityManagerFactoryUtils:419 - 关闭JPA   EntityManager 2017-11-17 13:58:50调试   ExceptionHandlerExceptionResolver:137 - 解决异常   handler [public com.smartcore.entities.mysqldb.UserMySql   com.smartcore.controllers.mysql.MySqlUserAccountController.findUserById(java.lang.Long中)〕:   org.springframework.http.converter.HttpMessageNotWritableException:   无法编写JSON:无法初始化代理 - 没有会话;嵌套   异常是com.fasterxml.jackson.databind.JsonMappingException:   无法初始化代理 - 没有会话2017-11-17 13:58:50调试   ResponseStatusExceptionResolver:137 - 解决处理程序中的异常   [public com.smartcore.entities.mysqldb.UserMySql   com.smartcore.controllers.mysql.MySqlUserAccountController.findUserById(java.lang.Long中)〕:   org.springframework.http.converter.HttpMessageNotWritableException:   无法编写JSON:无法初始化代理 - 没有会话;嵌套   异常是com.fasterxml.jackson.databind.JsonMappingException:   无法初始化代理 - 没有会话2017-11-17 13:58:50调试   DefaultHandlerExceptionResolver:137 - 解决处理程序中的异常   [public com.smartcore.entities.mysqldb.UserMySql   com.smartcore.controllers.mysql.MySqlUserAccountController.findUserById(java.lang.Long中)〕:   org.springframework.http.converter.HttpMessageNotWritableException:   无法编写JSON:无法初始化代理 - 没有会话;嵌套   异常是com.fasterxml.jackson.databind.JsonMappingException:   无法初始化代理 - 没有会话2017-11-17 13:58:50警告   DefaultHandlerExceptionResolver:380 - 无法写入HTTP消息:   org.springframework.http.converter.HttpMessageNotWritableException:   无法编写JSON:无法初始化代理 - 没有会话;嵌套   异常是com.fasterxml.jackson.databind.JsonMappingException:   无法初始化代理 - 没有会话2017-11-17 13:58:50调试   DispatcherServlet:1076 - Null ModelAndView返回   DispatcherServlet,名称为&#39; dispServlet&#39;:假设为HandlerAdapter   完成请求处理2017-11-17 13:58:50 DEBUG   DispatcherServlet:1004 - 成功完成请求

有什么不对?

1 个答案:

答案 0 :(得分:1)

您正在通过@Responsebody返回一个对象,您返回的userObject是代理对象,因为您使用的是“LAZY”提取。在控制器层中,不再有活动事务来获取实体对象的属性。

您可以EAGER获取对象,也可以将子属性声明为Transient