代码之家  ›  专栏  ›  技术社区  ›  nik

openSession(),获取当前会话(),开始事务(),延迟加载

  •  1
  • nik  · 技术社区  · 9 年前

    我有一个简单的java web应用程序,它从数据库接收一些信息,并在web浏览器中显示这些信息。Hibernate用于在servlet和jsp文件中与数据库交互。所有的工作都是我想要的,但有些事情我不懂。

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

    这是java类的代码问答:

    问题.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;
        }
    }
    

    答案.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>
    

    问题.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>
    

    答案.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>
    

    所以在问题中有一组答案。正因为如此,才会有一大堆懒惰的答案。我想在jsp文件中使用Hibernate对象,所以我使用 this technique :过滤器用于创建会话,该会话用于servlet和相应的jsp文件中。

    这是我的过滤器的代码:

    休眠过滤器.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的配置:

    获取一个问题.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>
    

    获取所有问题.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) 为什么我需要在servlet中调用“session.beginTransaction()”方法,即使我已经在过滤器中调用了“session.beginTraction()”?如果我从另一个servlet调用一个servlet,我还必须在第二个servlet中调用这个方法?或者我必须在每次与数据库交互之前调用此方法?

    2) 我没有在过滤器中调用“sessionFactory.getCurrentSession().openSession)”或“session Factory.getCurrentSeession().getCurrenSession”,但我在servlet中调用了“sessiionFactory/getCurrent会话().ggetCurrentsession(()”,并且仍然获得了在过滤器中创建的会话。这怎么可能?

    3) 如果我取消注释行“session.getTransaction().commit();”在jsp文件的GetOneQuestion类中,我收到LazyInitializationException:“无法初始化代理-没有会话”,即使这个jsp文件中没有延迟加载,因为我在那里没有使用任何Answer对象。此异常的原因是什么?会话必须打开,以便与Hibernate对象进行任何交互,即使没有延迟加载?

    1 回复  |  直到 9 年前
        1
  •  1
  •   v.ladynev    9 年前
    1. 您需要配置一次会话工厂(!)在应用程序启动时,而不是在每个servlet中。
    2. 事务提交后,您需要关闭过滤器中的会话。
    3. 您不需要在servlet中创建事务(或者不需要在过滤器中创建事务)。

    您可以将会话工厂存储在应用程序初始化器类的静态字段中,并在servlet中从中获取当前会话。

    关于您的问题

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