Why am I getting a TransactionRequiredException in

2019-07-30 23:18发布

问题:

I have Java EE project where I want to use an injected JTA EntityManager in the @PostConstruct method. EntityManager.persist fails due to javax.persistence.TransactionRequiredException. It succeeds when called through an EJB instance injected into a JSF managed bean. Manually starting a transaction with @Resource UserTransaction and UserTransaction.begin/commit or EntityManager.getTransaction.begin/commit because it's a JTA EntityManager.

The EJB interface

@Local
public interface UserService extends Serializable {

    public void saveUser(AUser user);
}

@Entity
public class AUser implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue
    private Long id;
    private String username;

    public AUser() {
    }

    public AUser(String username) {
        this.username = username;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

The EJB implementation:

@Stateless
public class DefaultUserService implements UserService {
    private static final long serialVersionUID = 1L;
    @PersistenceContext
    private EntityManager entityManager;

    public DefaultUserService() {
    }

    @PostConstruct
    private void init() {
        AUser user = new AUser("initUser");
        saveUser(user);
    }

    @Override
    public void saveUser(AUser user) {
        entityManager.persist(user);
    }
}

persistence.xml:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
  <persistence-unit name="richtercloud_javaee-persist-in-postconstruct-jar_jar_1.0-SNAPSHOTPU" transaction-type="JTA">
    <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
    <jta-data-source>jdbc/example1</jta-data-source>
    <class>richtercloud.javaee.persist.in.postconstruct.jar.entities.AUser</class>
    <properties>
      <property name="eclipselink.target-database" value="Derby"/>
      <!-- necessary in order to avoid syntax errors -->
      <property name="javax.persistence.schema-generation.database.action" value="create"/>
      <property name="eclipselink.ddl-generation" value="create-or-extend-tables"/>
      <property name="eclipselink.target-server" value="Glassfish"/>
      <!-- avoid non-severe NullPointerException being logged in GlasFish
            <ref>https://java.net/jira/browse/GLASSFISH-21468f</ref>-->
    </properties>
  </persistence-unit>
</persistence>

I didn't know how to provide the jdbc/example1 data source (it's GlassFish 4.1 JDBC resource based on a JDBC connection pool referring to a Derby database with network driver). Everything else it available at https://github.com/krichter722/javaee-persist-in-postconstruct.

I read Persisting in @PostConstruct: javax.persistence.TransactionRequiredException which exceeds the example of a simple EntityManager.persist call in @PostConstruct and http://www.tikalk.com/java/doing-transactional-work-spring-service-using-postconstruct-method/ which refers to Spring which I'm not using. I found no statement that @PersistenceContext EntityManager behaves differently in @PostConstruct.

回答1:

§8.6.2 of the EJB 3.2 specification states:

The term “an unspecified transaction context” is used in the EJB specification to refer to the cases in which the EJB architecture does not fully define the transaction semantics of an enterprise bean method execution.
This includes the following cases:
...
• The execution of a PostConstruct or PreDestroy callback method of a stateless session bean with container-managed transaction demarcation.

An alternative solution may be to specify the javax.persistence.sql-load-script-source property in your persistence.xml file. It points at a SQL script that will preload your database. This can be a resource embedded in your application or a file URL.



回答2:

it doesn't guarantee that @PostConstruct and @PreDestroy are a part of transaction. so no DB operations should be done in PostConstruct method as well as PreDestroy method.