views:

737

answers:

3

Hi All, I'm just starting to take a look at java persistence (at the moment with eclipse's default provider of eclipselink). Basically just creating an object and attempting to persist it to the db (Oracle). It was my understanding that the default transactionality should commit the new object to the database when the method returns but nothing seems to be happening. Any ideas?

@Stateless
public class RegisterUser implements RegisterUserLocal {

 @PersistenceContext
 private EntityManager entityManager;

    public void registerNewUser(String username, String password){
     User user = new User();
     user.setPassword(password);
     user.setUsername(username);
     entityManager.persist(user);
     entityManager.getTransaction().commit();
    }
}

Persistence.xml:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"&gt;
 <persistence-unit name="SCBCDEntities" transaction-type="RESOURCE_LOCAL">
  <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
  <class>examples.persistence.User</class>
  <properties>
   <property name="eclipselink.target-server" value="WebLogic_10"/>
   <property name="eclipselink.jdbc.driver" value="oracle.jdbc.OracleDriver"/>
   <property name="eclipselink.jdbc.url" value="jdbc:oracle:thin:@localhost:1521:db4"/>
   <property name="eclipselink.jdbc.user" value="SCBCD"/>
   <property name="eclipselink.jdbc.password" value="123456"/>
   <property name="eclipselink.logging.level" value="FINEST"/>
  </properties>
 </persistence-unit>
</persistence>

Entity Class:

@Entity
@Table(name="USERS")
public class User implements Serializable {
 private static final long serialVersionUID = 1L;

 @Id
 private String username;

 private String password;

    public User() {
    }

 public String getUsername() {
  return this.username;
 }

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

 public String getPassword() {
  return this.password;
 }

 public void setPassword(String password) {
  this.password = password;
 }

}

Also, to respond to replies to this question, with the code I've listed the logs show a commit executing (some detail removed for brevity)

[EL Finest]: 2010-01-05 22:58:07.468--UnitOfWork(25499586)--Thread(Thread[[ACTIVE] ExecuteThread: '0' for queue: 'weblogic.kernel.
Default (self-tuning)',5,Pooled Threads])--PERSIST operation called on: examples.persistence.User@191ed96.
<Jan 5, 2010 10:58:07 PM EST> <Debug> <JTA2PC> <BEA-000000> <BEA1-001959ECF50B251A451D: [EJB examples.session.stateless.RegisterUs
er.registerNewUser(java.lang.String,java.lang.String)]: ServerTransactionImpl.commit()>
<Jan 5, 2010 10:58:07 PM EST> <Debug> <JTA2PC> <BEA-000000> <BEA1-001959ECF50B251A451D: [EJB examples.session.stateless.RegisterUs
er.registerNewUser(java.lang.String,java.lang.String)]: TX[BEA1-001959ECF50B251A451D] active-->pre_preparing
<Jan 5, 2010 10:58:07 PM EST> <Debug> <JTA2PC> <BEA-000000> <SC[mr_domain+AdminServer] active-->pre-preparing
er.registerNewUser(java.lang.String,java.lang.String)]: TX[BEA1-001959ECF50B251A451D] prepared-->committing
<Jan 5, 2010 10:58:07 PM EST> <Debug> <JTA2PC> <BEA-000000> <SC[mr_domain+AdminServer] pre-prepared-->committed
er.registerNewUser(java.lang.String,java.lang.String)]: TX[BEA1-001959ECF50B251A451D] committing-->committed
...
...

but if I add 'flush' after the persist, I get 'notransaction'...

[EL Finest]: 2010-01-05 22:44:55.218--UnitOfWork(113017)--Thread(Thread[[ACTIVE] ExecuteThread: '2' for queue: 'weblogic.kernel.De
fault (self-tuning)',5,Pooled Threads])--PERSIST operation called on: examples.persistence.User@1717dea.
<Jan 5, 2010 10:44:55 PM EST> <Info> <EJB> <BEA-010227> <EJB Exception occurred during invocation from home or business: weblogic.
ejb.container.internal.StatelessEJBLocalHomeImpl@1509b8 threw exception: javax.persistence.TransactionRequiredException:
Exception Description: No transaction is currently active>
<Jan 5, 2010 10:44:55 PM EST> <Debug> <JTA2PC> <BEA-000000> <BEA1-001859ECF50B251A451D: [EJB examples.session.stateless.RegisterUs
er.registerNewUser(java.lang.String,java.lang.String)]: TX[BEA1-001859ECF50B251A451D] active-->rolling back
...
...
+1  A: 

This might be because you haven't begun a transaction. Try:

public void registerNewUser(String username, String password){
  entityManager.getTransaction().begin();
  User user = new User();
  user.setPassword(password);
  user.setUsername(username);
  entityManager.persist(user);
  entityManager.getTransaction().commit();
}

Although I prefer not to do transaction handling this way. Instead I tend to use Spring's declarative transactions, which would look like this:

@Transactional
public void registerNewUser(String username, String password){
  User user = new User();
  user.setPassword(password);
  user.setUsername(username);
  entityManager.persist(user);
}

when configured.

Edit: Another possibility is a problem I once had with EclipseLink where it wasn't writing everything to the database when I was running it in a J2SE environment (a console app to load some files into a database). In that case I had to explicitly flush() the EntityManager to get all the records written.

cletus
It's my understanding that you can use container managed transactions in the same way that your 2nd example does this, but using normal java instead of spring. I think this is supposed to be the default (if you don't specify anything), and that you can use the annotation @TransactionManagement(TransactionManagementType.BEAN) to specify manual handling.
MJ
With your 1st example, I have the following exception:Exception while registering user javax.ejb.EJBException: EJB Exception: : java.lang.IllegalStateException: The method public abstract javax.persistence.EntityTransaction javax.persistence.EntityManager.getTransaction() cannot be invoked in the context of a JTA EntityManager.
MJ
+1  A: 

You can not use the EntityTransaction (em.getTransaction()) because you are using a Container Managed Entity Manager (injected) so you must rely on the container for transactions. Have you set any transactional settings for the Stateless Session bean in XML? If you have not then it should work as the default should be 'Required'. You can try annotating 'registerNewUser' with @TransactionAttribute(TransactionAttributeType.REQUIRED)

Alternatively you could create a user transaction before accessing the EM but a Container Managed transaction would be better.

Gordon Yorke
A: 

After much investigation, including trying both container managed and user managed transactions, it appears that the problem is that the transaction-type is specified as RESOURCE_LOCAL. In this case the following rules apply:

*  You must use the EntityManagerFactory to get an EntityManager
* The resulting EntityManager instance is a PersistenceContext/Cache
* An EntityManagerFactory can be injected via the @PersistenceUnit annotation only (not @PersistenceContext)
* You are not allowed to use @PersistenceContext to refer to a unit of type RESOURCE_LOCAL
* You must use the EntityTransaction API to begin/commit around every call to your EntityManger
* Calling entityManagerFactory.createEntityManager() twice results in two separate EntityManager instances and therefor two separate PersistenceContexts/Caches.
* It is almost never a good idea to have more than one instance of an EntityManager in use (don't create a second one unless you've destroyed the first)

In my case, I needed to use the manager factory to access the entitymanager, and use a persistenceUnit instead of a persistenceContext. The following code works just fine:

@Stateless
@TransactionManagement(TransactionManagementType.CONTAINER)

public class RegisterUser implements RegisterUserLocal {

 @PersistenceUnit(unitName = "SCBCDEntities")
 private EntityManagerFactory factory;

 public void registerNewUser(String username, String password) {

  EntityManager entityManager = factory.createEntityManager();
  EntityTransaction entityTransaction = entityManager.getTransaction();
  entityTransaction.begin();

  User user = new User();
  user.setPassword(password);
  user.setUsername(username);

  entityManager.persist(user);
  entityTransaction.commit();
 }
}

Additional information about configuring transactions and the settings in the persistence.xml can be found here: http://openejb.apache.org/3.0/jpa-concepts.html

MJ