Java Hibernate/Spring:无法延迟初始化 - 没有会话或会话被关闭

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3041259/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 15:55:40  来源:igfitidea点击:

Hibernate/Spring: failed to lazily initialize - no session or session was closed

javahibernatespringlazy-loading

提问by Niko

For an answer scroll down to the end of this...

对于答案向下滚动到本文结尾...

The basic problem is the same as asked multiple time. I have a simple program with two POJOs Event and User - where a user can have multiple events.

基本问题与多次询问相同。我有一个带有两个 POJO 事件和用户的简单程序 - 用户可以有多个事件。

@Entity
@Table
public class Event {
 private Long id;
 private String name;
 private User user;

 @Column
 @Id
 @GeneratedValue
 public Long getId() {return id;}
 public void setId(Long id) { this.id = id; }

 @Column
 public String getName() {return name;}
 public void setName(String name) {this.name = name;}

 @ManyToOne
 @JoinColumn(name="user_id")
 public User getUser() {return user;}
 public void setUser(User user) {this.user = user;}

}

The User:

用户:

@Entity
@Table
public class User {
 private Long id;
 private String name;
 private List<Event> events;

 @Column
 @Id
 @GeneratedValue
 public Long getId() { return id; }
 public void setId(Long id) { this.id = id; }

 @Column
 public String getName() { return name; }
 public void setName(String name) { this.name = name; }

 @OneToMany(mappedBy="user", fetch=FetchType.LAZY)
 public List<Event> getEvents() { return events; }
 public void setEvents(List<Event> events) { this.events = events; }

}

Note: This is a sample project. I reallywant to use Lazy fetching here.

注意:这是一个示例项目。我真的很想在这里使用 Lazy fetching。

Now we need to configure spring and hibernate and have a simple basic-db.xml for loading:

现在我们需要配置 spring 和 hibernate 并有一个简单的 basic-db.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:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
           http://www.springframework.org/schema/aop 
           http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">


 <bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource"
  destroy-method="close"  scope="thread">
  <property name="driverClassName" value="com.mysql.jdbc.Driver" />
  <property name="url" value="jdbc:mysql://192.168.1.34:3306/hibernateTest" />
  <property name="username" value="root" />
  <property name="password" value="" />
  <aop:scoped-proxy/>
 </bean>

 <bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
  <property name="scopes">
   <map>
    <entry key="thread">
     <bean class="org.springframework.context.support.SimpleThreadScope" />
    </entry>
   </map>
  </property>
 </bean>

 <bean id="mySessionFactory"
  class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean" scope="thread">
  <property name="dataSource" ref="myDataSource" />
  <property name="annotatedClasses">
   <list>
    <value>data.model.User</value>
    <value>data.model.Event</value>
   </list>
  </property>
  <property name="hibernateProperties">
   <props>
    <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
    <prop key="hibernate.show_sql">true</prop>
    <prop key="hibernate.hbm2ddl.auto">create</prop>
   </props>
  </property>
  <aop:scoped-proxy/>

 </bean>

 <bean id="myUserDAO" class="data.dao.impl.UserDaoImpl">
  <property name="sessionFactory" ref="mySessionFactory" />
 </bean>

 <bean id="myEventDAO" class="data.dao.impl.EventDaoImpl">
  <property name="sessionFactory" ref="mySessionFactory" />
 </bean>

</beans>

Note: I played around with the CustomScopeConfigurer and SimpleThreadScope, but that didnt change anything.

注意:我使用了 CustomScopeConfigurer 和 SimpleThreadScope,但这并没有改变任何东西。

I have a simple dao-impl (only pasting the userDao - the EventDao is pretty much the same - except with out the "listWith" function:

我有一个简单的 dao-impl(只粘贴 userDao - EventDao 几乎相同 - 除了没有“listWith”函数:


public class UserDaoImpl implements UserDao{

 private HibernateTemplate hibernateTemplate;

 public void  setSessionFactory(SessionFactory sessionFactory) {
  this.hibernateTemplate = new HibernateTemplate(sessionFactory);

 }

 @SuppressWarnings("unchecked")
 @Override
 public List listUser() {
  return hibernateTemplate.find("from User");
 }

 @Override
 public void saveUser(User user) {
  hibernateTemplate.saveOrUpdate(user);

 }

 @Override
 public List listUserWithEvent() {

  List users = hibernateTemplate.find("from User");
  for (User user : users) {
   System.out.println("LIST : " + user.getName() + ":");
   user.getEvents().size();
  }
  return users;
 }

}

I am getting the org.hibernate.LazyInitializationException - failed to lazily initialize a collection of role: data.model.User.events, no session or session was closed at the line with user.getEvents().size();

我收到 org.hibernate.LazyInitializationException - 未能延迟初始化角色集合:data.model.User.events,没有会话或会话在user.getEvents().size()行关闭;

And last but not least here is the Test class I use:

最后但并非最不重要的是我使用的 Test 类:


public class HibernateTest {

 public static void main(String[] args) {

  ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("basic-db.xml");


  UserDao udao = (UserDao) ac.getBean("myUserDAO");
  EventDao edao = (EventDao) ac.getBean("myEventDAO");


  System.out.println("New user...");
  User user = new User();
  user.setName("test");

  Event event1 = new Event();
  event1.setName("Birthday1");
  event1.setUser(user);

  Event event2 = new Event();
  event2.setName("Birthday2");
  event2.setUser(user);

  udao.saveUser(user);
  edao.saveEvent(event1);
  edao.saveEvent(event2);

  List users = udao.listUserWithEvent();
  System.out.println("Events for users");
  for (User u : users) {

   System.out.println(u.getId() + ":" + u.getName() + " --");
   for (Event e : u.getEvents())
   {
    System.out.println("\t" + e.getId() + ":" + e.getName());
   }
  }

  ((ConfigurableApplicationContext)ac).close();
 }

}

and here is the Exception:

这是例外:

1621 [main] ERROR org.hibernate.LazyInitializationException - failed to lazily initialize a collection of role: data.model.User.events, no session or session was closed
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: data.model.User.events, no session or session was closed
 at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:380)
 at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:372)
 at org.hibernate.collection.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:119)
 at org.hibernate.collection.PersistentBag.size(PersistentBag.java:248)
 at data.dao.impl.UserDaoImpl.listUserWithEvent(UserDaoImpl.java:38)
 at HibernateTest.main(HibernateTest.java:44)
Exception in thread "main" org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: data.model.User.events, no session or session was closed
 at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:380)
 at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:372)
 at org.hibernate.collection.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:119)
 at org.hibernate.collection.PersistentBag.size(PersistentBag.java:248)
 at data.dao.impl.UserDaoImpl.listUserWithEvent(UserDaoImpl.java:38)
 at HibernateTest.main(HibernateTest.java:44)

Things tried but did not work:

事情尝试但没有奏效:

  • assign a threadScope and using beanfactory (I used "request" or "thread" - no difference noticed):
  • 分配一个 threadScope 并使用 beanfactory(我使用了“请求”或“线程” - 没有注意到区别):
  // scope stuff
  Scope threadScope = new SimpleThreadScope();
  ConfigurableListableBeanFactory beanFactory = ac.getBeanFactory();
  beanFactory.registerScope("request", threadScope);
  ac.refresh();
...
  • Setting up a transaction by getting the session object from the deo:
  • 通过从 deo 获取会话对象来设置事务:
...
  Transaction tx = ((UserDaoImpl)udao).getSession().beginTransaction();
  tx.begin();
  users = udao.listUserWithEvent();
...
  • getting a transaction within the listUserWithEvent()
  • 在 listUserWithEvent() 中获取交易
 public List listUserWithEvent() {
  SessionFactory sf = hibernateTemplate.getSessionFactory();
  Session s = sf.openSession();
  Transaction tx = s.beginTransaction();
  tx.begin();

  List users = hibernateTemplate.find("from User");
  for (User user : users) {
   System.out.println("LIST : " + user.getName() + ":");
   user.getEvents().size();
  }
  tx.commit();
  return users;
 }

I am really out of ideas by now. Also, using the listUser or listEvent just work fine.

我现在真的没有想法了。此外,使用 listUser 或 listEvent 也可以正常工作。

Step forward:

向前一步:

Thanks to Thierry I got one step further (I think). I created the MyTransaction class and do my whole work in there, getting everything from spring. The new main looks like this:

感谢蒂埃里,我又向前迈进了一步(我认为)。我创建了 MyTransaction 类并在那里完成我的全部工作,从 spring 获取所有内容。新的主看起来像这样:


 public static void main(String[] args) {

  ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("basic-db.xml");

  // getting dao
  UserDao udao = (UserDao) ac.getBean("myUserDAO");
  EventDao edao = (EventDao) ac.getBean("myEventDAO");

  // gettting transaction template
  TransactionTemplate transactionTemplate = (TransactionTemplate) ac.getBean("transactionTemplate");

  MyTransaction mt = new MyTransaction(udao, edao);
  transactionTemplate.execute(mt);

  ((ConfigurableApplicationContext)ac).close();
 }

Unfortunately now there is a null-pointer Exception @: user.getEvents().size(); (in the daoImpl).

不幸的是现在有一个空指针异常@: user.getEvents().size(); (在 daoImpl 中)。

I know that it should not be null (neither from the output in the console nor from the db layout).

我知道它不应该为空(既不是来自控制台的输出也不是来自 db 布局)。

Here is the console output for more information (I did a check for user.getEvent() == null and printed "EVENT is NULL"):

这是更多信息的控制台输出(我检查了 user.getEvent() == null 并打印了“EVENT is NULL”):

New user...
Hibernate: insert into User (name) values (?)
Hibernate: insert into User (name) values (?)
Hibernate: insert into Event (name, user_id) values (?, ?)
Hibernate: insert into Event (name, user_id) values (?, ?)
Hibernate: insert into Event (name, user_id) values (?, ?)
List users:
Hibernate: select user0_.id as id0_, user0_.name as name0_ from User user0_
1:User1
2:User2
List events:
Hibernate: select event0_.id as id1_, event0_.name as name1_, event0_.user_id as user3_1_ from Event event0_
1:Birthday1 for 1:User1
2:Birthday2 for 1:User1
3:Wedding for 2:User2
Hibernate: select user0_.id as id0_, user0_.name as name0_ from User user0_
Events for users
1:User1 --
EVENT is NULL
2:User2 --
EVENT is NULL

You can get the sample project from http://www.gargan.org/code/hibernate-test1.tgz(it's an eclipse/maven project)

您可以从http://www.gargan.org/code/hibernate-test1.tgz获取示例项目(这是一个 eclipse/maven 项目)

The solution (for console applications)

解决方案(用于控制台应用程序)

There are actually two solutions for this problem - depending on your environment:

这个问题实际上有两种解决方案 - 取决于您的环境:

For a console application you need a transaction template which captures the actutal db logic and takes care of the transaction:

对于控制台应用程序,您需要一个事务模板来捕获实际的数据库逻辑并处理事务:


public class UserGetTransaction implements TransactionCallback{

 public List users;

 protected ApplicationContext context;

 public UserGetTransaction (ApplicationContext context) {
  this.context = context;
 }

 @Override
 public Boolean doInTransaction(TransactionStatus arg0) {
  UserDao udao = (UserDao) ac.getBean("myUserDAO");
  users = udao.listUserWithEvent();
  return null;
 }

}

You can use this by calling:

您可以通过调用来使用它:


 TransactionTemplate transactionTemplate = (TransactionTemplate) context.getBean("transactionTemplate");
 UserGetTransaction mt = new UserGetTransaction(context);
 transactionTemplate.execute(mt);

In order for this to work you need to define the template class for spring (ie. in your basic-db.xml):

为了使其工作,您需要为 spring 定义模板类(即在您的 basic-db.xml 中):

<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
    <property name="transactionManager" ref="transactionManager"/>
</bean>

Another (possible) solution

另一个(可能的)解决方案

thanks andi

谢谢安迪

    PlatformTransactionManager transactionManager = (PlatformTransactionManager) applicationContext.getBean("transactionManager");
    DefaultTransactionAttribute transactionAttribute = new DefaultTransactionAttribute(TransactionDefinition.PROPAGATION_REQUIRED);

transactionAttribute.setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE);
    TransactionStatus status = transactionManager.getTransaction(transactionAttribute);
    boolean success = false;
    try {
      new UserDataAccessCode().execute();
      success = true;
    } finally {
      if (success) {
        transactionManager.commit(status);
      } else {
        transactionManager.rollback(status);
      }
    }

The solution (for servlets)

解决方案(对于 servlet)

Servlets are not that big of a problem. When you have a servlet you can simply start and bind a transaction at the beginning of your function and unbind it again at the end:

Servlet 并不是什么大问题。当您有一个 servlet 时,您可以简单地在函数的开头启动并绑定一个事务,并在最后再次取消绑定:

public void doGet(...) {
  SessionFactory sessionFactory = (SessionFactory) context.getBean("sessionFactory");
  Session session = SessionFactoryUtils.getSession(sessionFactory, true);
  TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));

// Your code....

  TransactionSynchronizationManager.unbindResource(sessionFactory);
}

采纳答案by Thierry

I think you should not use the hibernate session transactional methods, but let spring do that.

我认为您不应该使用休眠会话事务方法,而是让 spring 这样做。

Add this to your spring conf:

将此添加到您的弹簧配置中:

<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="mySessionFactory" />
</bean>

<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
    <property name="transactionManager" ref="txManager"/>
</bean>

and then I would modify your test method to use the spring transaction template:

然后我将修改您的测试方法以使用 spring 事务模板:

public static void main(String[] args) {
    // init here (getting dao and transaction template)

    transactionTemplate.execute(new TransactionCallback() {
        @Override
        public Object doInTransaction(TransactionStatus status) {
          // do your hibernate stuff in here : call save, list method, etc
        }
    }
}

as a side note, @OneToMany associations are lazy by default, so you don't need to annotate it lazy. (@*ToMany are LAZY by default, @*ToOne are EAGER by default)

作为旁注,@OneToMany 关联默认情况下是惰性的,因此您无需对其进行惰性注释。(@*ToMany 默认为 LAZY,@*ToOne 默认为 EAGER)

EDIT: here is now what is happening from hibernate point of view:

编辑:现在从休眠的角度来看正在发生的事情:

  • open session (with transaction start)
  • save a user and keep it in the session (see the session cache as an entity hashmap where the key is the entity id)
  • save an event and keep it in the session
  • save another event and keep it in the session
  • ... same with all the save operations ...

  • then load all users (the "from Users" query)

  • at that point hibernate see that it has already the object in its session, so discard the one it got from the request and return the one from the session.
  • your user in the session does not have its event collection initialized, so you get null.
  • ...
  • 打开会话(事务开始)
  • 保存用户并将其保留在会话中(将会话缓存视为实体哈希图,其中键是实体 ID)
  • 保存事件并将其保留在会话中
  • 保存另一个事件并将其保留在会话中
  • ...与所有保存操作相同...

  • 然后加载所有用户(“来自用户”查询)

  • 在这一点上,hibernate 看到它已经在其会话中拥有该对象,因此丢弃它从请求中获得的对象并从会话中返回该对象。
  • 您在会话中的用户没有初始化其事件集合,因此您会得到 null。
  • ...

Here are some points to enhance your code:

以下是增强代码的一些要点:

  • in your model, when collection ordering is not needed, use Set, not List for your collections (private Set events, not private List events)
  • in your model, type your collections, otherwise hibernate won't which entity to fetch (private Set<Event> events)
  • when you set one side of a bidirectional relation, and you wish to use the mappedBy side of the relation in the same transaction, set both sides. Hibernate will not do it for you before the next tx (when the session is a fresh view from the db state).
  • 在您的模型中,当不需要集合排序时,请为您的集合使用 Set,而不是 List(私有 Set 事件,而不是私有 List 事件)
  • 在您的模型中,输入您的集合,否则休眠将不会获取哪个实体(私有 Set<Event> 事件)
  • 当您设置双向关系的一侧,并且您希望在同一事务中使用该关系的 mapsBy 侧时,请设置两侧。Hibernate 不会在下一个 tx 之前为你做这件事(当会话是从数据库状态的新视图时)。

So to address the point above, either do the save in one transaction, and the loading in another one :

因此,为了解决上述问题,要么在一个事务中保存,要么在另一个事务中加载:

public static void main(String[] args) {
    // init here (getting dao and transaction template)
    transactionTemplate.execute(new TransactionCallback() {
        @Override
        public Object doInTransaction(TransactionStatus status) {
          // save here
        }
    }

    transactionTemplate.execute(new TransactionCallback() {
        @Override
        public Object doInTransaction(TransactionStatus status) {
          // list here
        }
    }
}

or set both sides:

或设置双方:

...
event1.setUser(user);
...
event2.setUser(user);
...
user.setEvents(Arrays.asList(event1,event2));
...

(Also do not forget to address the code enhancement points above, Set not List, collection typing)

(另外不要忘记解决上面的代码增强点,Set not List,collection typing)

回答by Gray

The issue is that your dao is using one hibernate session but the lazy load of the user.getName (I assume that is where it throws) is happening outside that session -- either not in a session at all or in another session. Typically we open up a hibernate session beforewe make DAO calls and don't close it until we are done with all lazy loads. Web requests are usually wrapped in a big session so these problems do not happen.

问题是您的 dao 正在使用一个休眠会话,但是 user.getName 的延迟加载(我假设这是它抛出的地方)发生在该会话之外 - 要么根本不在会话中,要么在另一个会话中。通常,我们在进行DAO 调用之前打开一个休眠会话并且在完成所有延迟加载之前不要关闭它。Web 请求通常包含在一个大会话中,因此这些问题不会发生。

Typically we have wrapped our dao and lazy calls in a SessionWrapper. Something like the following:

通常,我们将 dao 和延迟调用包装在 SessionWrapper 中。类似于以下内容:

public class SessionWrapper {
    private SessionFactory sessionFactory;
    public void setSessionFactory(SessionFactory sessionFactory) {
        this.hibernateTemplate = new HibernateTemplate(sessionFactory);
    }
    public <T> T runLogic(Callable<T> logic) throws Exception {
        Session session = null;
        // if the session factory is already registered, don't do it again
        if (TransactionSynchronizationManager.getResource(sessionFactory) == null) {
            session = SessionFactoryUtils.getSession(sessionFactory, true);
            TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));
        }

        try {
            return logic.call();
        } finally {
            // if we didn't create the session don't unregister/release it
            if (session != null) {
                TransactionSynchronizationManager.unbindResource(sessionFactory);
                SessionFactoryUtils.releaseSession(session, sessionFactory);
            }
        }
    }
}

Obviously the SessionFactory the same SessionFactory that was injected into your dao.

显然 SessionFactory 与注入到您的 dao 中的 SessionFactory 相同。



In your case, you should wrap the entire listUserWithEvent body in this logic. Something like:

在您的情况下,您应该将整个 listUserWithEvent 主体包装在此逻辑中。就像是:

public List listUserWithEvent() {
    return sessionWrapper.runLogic(new Callable<List>() {
        public List call() {
            List users = hibernateTemplate.find("from User");
            for (User user : users) {
                System.out.println("LIST : " + user.getName() + ":");
                user.getEvents().size();
            }
        }
    });
}

You will need to inject the SessionWrapper instance into your daos.

您需要将 SessionWrapper 实例注入到您的 daos 中。

回答by Fran Jiménez

I got here looking for a hint regarding a similar problem. I tried the solution mentioned by Thierry and it didnt work. After that I tried these lines and it worked:

我来到这里寻找有关类似问题的提示。我尝试了 Thierry 提到的解决方案,但没有奏效。之后,我尝试了这些行并且它起作用了:

SessionFactory sessionFactory = (SessionFactory) context.getBean("sessionFactory");
Session session = SessionFactoryUtils.getSession(sessionFactory, true);
TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));

Indeed what I'm doing is a batch process that must leverage Spring existings managers/services. After loading the context and doing some invocations I founded the famous issue "failed to lazily initialize a collection". Those 3 lines solved it for me.

事实上,我正在做的是一个批处理,它必须利用 Spring 现有的管理器/服务。在加载上下文并进行一些调用后,我发现了著名的问题“无法延迟初始化集合”。这 3 行为我解决了它。

回答by weekens

In case of Web application, it is also possible to declare a special Filter in web.xml, that will do session-per-request:

在 Web 应用程序的情况下,也可以在 web.xml 中声明一个特殊的过滤器,它将执行 session-per-request:

<filter>
    <filter-name>openSessionInViewFilter</filter-name>
    <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
</filter>

<filter-mapping>
    <filter-name>openSessionInViewFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

After that you can lazyload your data anytime during the request.

之后,您可以在请求期间随时延迟加载数据。

回答by mhimu

Interesting!

有趣的!

I had the same problem in a @Controller's @RequestMapping handler method. The simple solution was to add a @Transactional annotation to the handler method so that the session is kept open for the whole duration of the method body execution

我在 @Controller 的 @RequestMapping 处理程序方法中遇到了同样的问题。简单的解决方案是向处理程序方法添加@Transactional 注释,以便会话在方法主体执行的整个持续时间内保持打开状态

回答by ydntn

Easiest solution to implement:

最容易实施的解决方案:

Within the scope of the session[inside the API annotated with @Transactional], do the following:

在会话范围内[使用@Transactional 注释的API 内],执行以下操作:

if A had a List<B> which is lazily loaded, simply call an API which makes sure the List is loaded

如果 A 有一个延迟加载的 List<B>,只需调用一个 API 来确保加载了 List

What's that API ?

那个 API 是什么?

size(); API of the List class.

尺寸(); List 类的 API。

So all that's needed is:

所以只需要:

Logger.log(a.getBList.size());

Logger.log(a.getBList.size());

This simple call of logging the size makes sure it gets the whole list before calculating the size of the list. Now you will not get the exception !

这个记录大小的简单调用确保它在计算列表大小之前获取整个列表。现在你不会得到异常了!

回答by EpicPandaForce

What worked for us in JBoss was the solution #2 taken from this site at Java Code Geeks.

在 JBoss 中对我们有用的是从Java Code Geeks 网站上获取的解决方案 #2 。

Web.xml:

网页.xml:

  <filter>
      <filter-name>ConnectionFilter</filter-name>
      <filter-class>web.ConnectionFilter</filter-class>
  </filter>
  <filter-mapping>
      <filter-name>ConnectionFilter</filter-name>
      <url-pattern>/faces/*</url-pattern>
  </filter-mapping>

ConnectionFilter:

连接过滤器:

import java.io.IOException;
import javax.annotation.Resource;
import javax.servlet.*;
import javax.transaction.UserTransaction;

public class ConnectionFilter implements Filter {
    @Override
    public void destroy() { }

    @Resource
    private UserTransaction utx;

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        try {
            utx.begin();
            chain.doFilter(request, response);
            utx.commit();
        } catch (Exception e) { }
    }

    @Override
    public void init(FilterConfig arg0) throws ServletException { }
}

Maybe it would work with Spring too.

也许它也适用于 Spring。