openSession(),getCurrentSession(),beginTransaction(),延迟加载

时间:2016-01-15 10:42:57

标签: java hibernate jsp servlets

我有一个简单的java Web应用程序,它从数据库接收一些信息并在Web浏览器中显示该信息。 Hibernate用于与servlet和jsp文件中的数据库进行交互。一切都按我的意愿行事,但我不了解一些事情。

数据库很简单 - 2个表:问题和答案。表之间的关系是一对多的:一个问题可以有很多答案。

这是java类的代码问答:

Question.java

package app;

import java.util.Set;

public class Question {
    Long id = null;
    String text = "";
    Set<Answer> answers = null;

    public Question() {        
    }    

    public void setId(Long id) {
        this.id = id;
    }
    public void setText(String text) {
        this.text = text;
    }
    public void setAnswers(Set<Answer> answers) {
        this.answers = answers;
    }
    public Long getId() {
        return id;
    }
    public String getText() {
        return text;
    }
    public Set<Answer> getAnswers() {
        return answers;
    }
}

Answer.java

package app;

public class Answer {
    Long id = null;    
    String text = "";
    Question question = null;

    public Answer() {
    }
    public void setId(Long id) {
        this.id = id;        
    }
    public void setText(String text) {
        this.text = text;
    }
    public void setQuestion(Question question) {
        this.question = question;
    }    
    public Long getId() {
        return id;
    }
    public String getText() {
        return text;
    }
    public Question getQuestion() {
        return question;
    } 
}

这是Hibernate的配置:

hibernate.cfg.xml中

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
  <session-factory>
    <property name="hibernate.connection.driver_class">com.microsoft.sqlserver.jdbc.SQLServerDriver</property>
    <property name="hibernate.connection.url">jdbc:sqlserver://localhost;databaseName=Test</property>
    <property name="hibernate.connection.username">sa</property>
    <property name="hibernate.connection.password">password</property>

    <property name="hibernate.current_session_context_class">thread</property>

    <mapping resource="question.hbm.xml"/>
    <mapping resource="answer.hbm.xml"/>
  </session-factory>
</hibernate-configuration>

question.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="app">
    <class name="app.Question" table="Question">
        <id column="id" name="id" type="java.lang.Long">
            <generator class="native"/>
        </id>
        <property column="text" name="text" not-null="true" type="java.lang.String"/>
        <set name="answers">
            <key column="question_id"/>
            <one-to-many class="app.Answer"/>
        </set>
    </class>
</hibernate-mapping>

answer.hbm.xml

    <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="app">
    <class name="app.Answer" table="Answer">
        <id column="id" name="id" type="java.lang.Long">
            <generator class="native"/>
        </id>
        <property column="text" name="text" not-null="true" type="java.lang.String"/>
        <many-to-one class="app.Question" column="question_id" name="question" not-null="true"/>
    </class>
</hibernate-mapping>

因此在问题中有一系列答案。因此,会有一些懒惰的Answers。我想在我的jsp文件中使用Hibernate对象,所以我使用this technique:过滤器用于创建会话,此会话用于servlet和相应的jsp文件。

这是我的过滤器的代码:

HibernateFilter.java

package app;

import java.io.IOException;
import javax.servlet.*;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class HibernateFilter implements Filter {  

    private SessionFactory sessionFactory;  

    public void doFilter(ServletRequest request,  
                         ServletResponse response,  
                         FilterChain chain)  
            throws IOException, ServletException {    
        try {
            sessionFactory.getCurrentSession().beginTransaction();  
            chain.doFilter(request, response);  
            sessionFactory.getCurrentSession().getTransaction().commit();  
        } catch (Exception ex) { 
            sessionFactory.getCurrentSession().getTransaction().rollback();
            ex.printStackTrace();   
        }  
    }  

    public void init(FilterConfig filterConfig) throws ServletException { 
        sessionFactory = new Configuration().configure().buildSessionFactory();  
    }  

    public void destroy() {
    }    
}  

我的应用程序中有两个servlet和两个相应的jsp文件 - 第一个显示ID = 1的问题,第二个显示所有问题。这是这个servlet的代码,相应的jsp文件和tomcat的配置:

GetOneQuestion.java

package app;

import java.io.IOException;
import javax.servlet.*;
import javax.servlet.http.*;
import org.hibernate.Session;
import org.hibernate.cfg.Configuration;

public class GetOneQuestion extends HttpServlet { 

    @Override
    public void doGet(HttpServletRequest request, 
            HttpServletResponse response) 
            throws ServletException, IOException {  
        try {
            Session session = new Configuration().configure()
                    .buildSessionFactory().getCurrentSession();

            session.beginTransaction();
            Question question = (Question)session.load(Question.class, 1L);            
            //session.getTransaction().commit();

            request.setAttribute("oneQuestion", question); 
        } catch (Exception ex) {            
            ex.printStackTrace();
            request.setAttribute("oneQuestion", null);
        }             
        RequestDispatcher view = request.getRequestDispatcher("/oneQuestion.jsp");      
        view.forward(request, response);
    }                                                                               
}

oneQuestion.jsp

<%@page import="app.Answer"%>
<%@page import="app.Question"%>
<html>
    <body>        
        <%
            Question question = (Question)request.getAttribute("oneQuestion");            
            out.print("<br>" + question.getText() + "<br><br>");             
        %>           
    </body>
</html>

GetAllQuestion.java

package app;

import java.io.IOException;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.List;
import org.hibernate.*;
import org.hibernate.cfg.Configuration;

public class GetAllQuestion extends HttpServlet { 

    @Override
    public void doGet(HttpServletRequest request, 
            HttpServletResponse response) 
            throws ServletException, IOException { 
        try {                 
            Session session = new Configuration().configure()
                    .buildSessionFactory().getCurrentSession();

            session.beginTransaction();        
            Query query = session.createQuery("from Question"); 
            List all = query.list();                    

            request.setAttribute("allQuestion", all);                                                        
        } catch (Exception ex) {            
            ex.printStackTrace();
            request.setAttribute("allQuestion", null);
        }             
        RequestDispatcher view = request.getRequestDispatcher("/allQuestion.jsp");      
        view.forward(request, response);
    }                                                                               
}

allQuestion.jsp

<%@page import="app.Answer"%>
<%@page import="app.Question"%>
<%@page import="java.util.List"%>
<html>
    <body>       
        <%
            List all = (List)request.getAttribute("allQuestion");

            for (Object object : all) {
                Question question = (Question)object;
                out.print("<br>Question " + question.getId());   

                for (Answer answer : question.getAnswers()) {
                    out.print("<br>" + answer.getText() + "<br>");  
                }
            }
        %>           
    </body>
</html>

的web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.1" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
    <filter>
        <filter-name>HibernateFilter</filter-name>
        <filter-class>app.HibernateFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>HibernateFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <servlet>
        <servlet-name>getAllQuestion</servlet-name>
        <servlet-class>app.GetAllQuestion</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>getAllQuestion</servlet-name>
        <url-pattern>/getAllQuestion</url-pattern>
    </servlet-mapping>
    <servlet>
        <servlet-name>getOneQuestion</servlet-name>
        <servlet-class>app.GetOneQuestion</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>getOneQuestion</servlet-name>
        <url-pattern>/getOneQuestion</url-pattern>
    </servlet-mapping>
    <session-config>
        <session-timeout>
            30
        </session-timeout>
    </session-config>
    <welcome-file-list>
        <welcome-file>index.html</welcome-file>
    </welcome-file-list>
</web-app>

问题

1)为什么我需要调用&#34; session.beginTransaction()&#34; servlet中的方法,即使我已经调用&#34; session.beginTransaction()&#34;在过滤器?如果我从另一个servler调用一个servler,我也必须在第二个servlet中调用这个方法?或者我必须在每次与数据库交互之前调用此方法?

2)我没有调用&#34; sessionFactory.getCurrentSession()。openSession()&#34;或&#34; sessionFactory.getCurrentSession()。getCurrentSession()&#34;在过滤器中,我调用&#34; sessionFactory.getCurrentSession()。getCurrentSession()&#34;在servlet中仍然获得似乎在过滤器中创建的会话。怎么会这样?

3)如果我取消注释line&#34; session.getTransaction()。commit();&#34;在来自jsp文件的GetOneQuestion类中,我收到了LazyInitializationException:&#34;无法初始化代理 - 没有Session&#34;即使这个jsp文件中没有延迟加载,因为我不在那里使用任何Answer对象。是什么导致这种例外?即使没有延迟加载,Session也必须打开与Hibernate对象的任何交互?

1 个答案:

答案 0 :(得分:1)

  1. 您需要在应用程序启动时配置一次会话工厂(!),而不是在每个servlet中配置。
  2. 您需要在事务提交后关闭过滤器中的会话。
  3. 您不需要在servlet中创建事务(或者您不需要在过滤器中创建事务)。
  4. 您可以将会话工厂存储在应用程序初始化程序类的静态字段中,并从servlet中获取当前会话。

    关于您的问题

    1. 因为你做错了。您在servlet中创建一个新的会话工厂。
    2. 您获得了一个新会话(因为您创建了不正确的会话工厂)。在正常情况下getCurrentSession()返回绑定到当前线程的会话ThreadLocal(并非总是如此,它取决于配置)。
    3. LazyInitializationException question.getAnswers()