'no session or session was closed' with JPA 2 and EJB 3.1

你说的曾经没有我的故事 提交于 2019-12-25 06:49:56

问题


I have stateless session bean with this method:

@Override
public List<Character> getUserCharacters(int userId) {
    User user = em.find(User.class, userId);
    if(user != null)
        return user.getCharacters();
    else
        return null;
}

where User class if defined in this way:

@Entity
@Table(name="Users")
public class User implements Serializable{

    /**  */

private static final long serialVersionUID = 8119486011976039947L;

@Id
@GeneratedValue(strategy=GenerationType.SEQUENCE)
private int id;

@ManyToMany(fetch=FetchType.EAGER)
private Set<Role> roles;

@OneToMany(mappedBy="owner",fetch=FetchType.LAZY)
private List<com.AandP.game.model.characters.Character> characters;

public User() {
    creationDate = new Date();
    roles = new HashSet<Role>();
    }
}

But when i execute this method (from my @Named bean) i receive exception:

 org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.AandP.game.model.User.characters, no session or session was closed

According to the JPA 2.0 specification session should stay alive for a whole transaction. In this situation transaction (in my opinion) last for a whole method call (there is no additional transaction attributes on class or method).

So the question is: what is wrong with this code and how can I load class properties in a lazy way.


回答1:


According to the JPA 2.0 specification session should stay alive for a whole transaction. In this situation transaction (in my opinion) last for a whole method call (there is no additional transaction attributes on class or method).

That's true, but it does not include the serialization of the returned objects.

I had the same issue exporting a session bean as a web service. Read more about the issue here.

If you have a similar use would strongly suggest you to return plain objects and not entities. You can use some bean mapping framework like we did. We used Dozer.




回答2:


You need to specify @TransactionAttribute so that your method is transactional. Otherwise a readonly transaction and a new underlying session is started for each entity manager operation. This, combined with a lazy collection, means that when you fetch the collection the original session gets closed.




回答3:


pWoz: What Bozho says is correct. The one caveat I'd add is if you're not entering into that method through an interface (Local or Remote), that annotation doesn't matter. If this is what your bean looks like:

@Stateless
public class MyUserBean implements UserBeanLocal {
...

  public void doSomeStuffWithUserCharactersById(int id) {
     List<Character>userCharacters = getUserCharacters(id);
  }

  @Override
  @TransactionAttribute(TransactionAttributeType.REQUIRED)
  public List<Character> getUserCharacters(int userId) {
   User user = em.find(User.class, userId);
   if(user != null)
     return user.getCharacters();
    else
     return null;
  }
}

That attribute's not going to be honored because within the same session bean, the first business method invoked determines the transaction context and behavior. Now, I can't tell for certain if that's what's going on in this instance because I don't see the rest of your source code, but that's a very common mistake.




回答4:


2013-07-24 19:47:07,387 ERROR: - failed to lazily initialize a collection of role: com.xxxx.domain.DenialMaster.denialDetails, no session or session was closed null

My config:

<tx:annotation-driven transaction-manager="transactionManagerDL" />

<bean id="transactionManagerDL" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="emfDL" />
</bean>

<bean id="emfDL" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="dataSourceDL" />
    <property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
    </property>
    <property name="mappingResources">
        <value>META-INF/orm.xml</value>
    </property>
    <property name="packagesToScan" value="${dl.entity.packages}" />
    <property name="jpaProperties">
        <props>
            <prop key="hibernate.dialect">${catalog.org.hibernate.dialect}</prop>
            <prop key="hibernate.max_fetch_depth">${catalog.org.hibernate.jdbc.max.fetchdepth}</prop>
            <prop key="hibernate.jdbc.fetch_size">${catalog.org.hibernate.jdbc.fetchsize}</prop>
            <prop key="hibernate.jdbc.batch_size">${catalog.org.hibernate.jdbc.batchsize}</prop>
            <prop key="hibernate.show_sql">${catalog.org.hibernate.showsql}</prop>
            <prop key="hibernate.connection.autocommit">${catalog.org.hibernate.connection.autocommit}
            </prop>

        </props>
    </property>
</bean>

SOLUTION: In your Class ServiceImpl add the @Transactional(readOnly = true) Example:

@Override
@Transactional(readOnly = true)
public YourBean findById(long id) throws Exception {
    return yourDAO.findOne(id);
}

and @LazyCollection(LazyCollectionOption.FALSE):

  @OneToMany(fetch=FetchType.LAZY)
  @JoinColumn(name = "idMaster")
  @LazyCollection(LazyCollectionOption.FALSE)
  public List<DenialDetail> getDenialDetails() {
    return denialDetails;
  }


来源:https://stackoverflow.com/questions/9624725/no-session-or-session-was-closed-with-jpa-2-and-ejb-3-1

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!