提问者:小点点

为什么会话在Spring控制器中无效,使用SpringMVC实现REST的最佳方式是什么?


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

无法写入JSON:无法初始化代理-没有会话;

我已经找到了解决办法如何防止这种异常的下一个链接

JsonMappingException:无法初始化代理-没有会话

@Proxy(懒=假)做什么?

@代理(懒=假)-已经解决了我的问题,但我真的不明白为什么当我尝试获取用户时,Spring控制器中的会话会关闭?

当我持久化用户时-没有这样的例外!尽管如此,相同的服务用于持久化和获取对象。怎么可能呢?

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

org. Spring框架.web.ser v let.DispatcherS er v let

这个servlet将请求和传输映射到我的控制器。我正在使用Spring json消息转换器

org. Spring框架.http.转换器.json.映射Jackson 2 Http消息转换器

Spring的调度服务器的配置是

 <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)的原生方法来检索用户。

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>

已添加应用程序上下文. 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(懒=假)-导致从任何对象到我的对象的实时链接包含它,它会导致内存消耗增加吗?

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

更新

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

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>

HibernateAware对象

public class HibernateAwareObjectMapper extends ObjectMapper {

    private static final long serialVersionUID = 5371945173448137189L;

    public HibernateAwareObjectMapper() {

        this.registerModule(new Hibernate5Module());

    }

}

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

能有什么错?


共1个答案

匿名用户

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

您可以执行EAGER获取对象或将子属性声明为瞬态