Using Eclipse with Google plugin:
- to run application locally: Google->Compile GWT (red toolbox toolbar icon) and run as Run As->Web Application
JpaDao {
public void persist(E entity) {
if (entity.getId() == null) {
entityManager.persist(entity);
} else {
entityManager.merge(entity);
}
}
}as suggested by Marcell Manfrin (in a comment).@Entity
public class Book implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String title;
private float price;
public Book() {
super();
}
...
}
Here, I have annotated the property, rather than the getter (not shown for brevity), since it feels more intuitive to me. I don't know if there is any advantage/difference of one vs the other other than my personal preference. The "@GeneratedValue(...)" annotation for primary key in JPA entity definition is not optional, as I expected. Using just the "@Id" annotation results in the primary key being not set (set to 0).@Remote
public interface BookManager {
public abstract int addBook(Book book);
public abstract Book find(int bookId);
}
@Stateless(name="BookManager", mappedName = "ejb/BookManagerJNDI")
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public class BookManagerImpl implements BookManager {
// Dependency injection of Entity Manager for
// the given persistence unit
@PersistenceContext(unitName="pu1") EntityManager em;
public int addBook(Book book) {
// Transitions new instances to managed. On the
// next flush or commit, the newly persisted
// instances will be inserted into the datastore.
em.persist(book);
return book.getId();
}
@Override
public Book find(int bookId) {
return em.find(Book.class, bookId);
}
}
TransactionAttributeType.REQUIRED tells the container to wrap each method in the class with a transaction. JPA Setup<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="1.0">
<persistence-unit name="pu1">
<!-- Persistence provider -->
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>jdbc/__default</jta-data-source>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.DerbyDialect" />
<property name="hibernate.hbm2ddl.auto" value="create" />
<property name="hibernate.show_sql" value="true" />
</properties>
</persistence-unit>
</persistence>
try {
InitialContext ctx = new InitialContext();
BookManager bean = (BookManager) ctx.lookup("ejb/BookManagerJNDI");
Book book = new Book("Chocolate Rain", 25.10f);
int bookId = bean.addBook(book );
book = null;
book = bean.find(bookId);
Assert.assertEquals("Chocolate Rain", book.getTitle());
} catch (NamingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
Assert.fail("Failed. Exception thrown.");
}
Session session = factory.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
// Do some work
session.load(...);
session.persist(...);
tx.commit(); // Flush happens automatically
}
catch (RuntimeException e) {
tx.rollback();
throw e; // or display error message
}
finally {
session.close();
}
You choose the approach.
UserTransaction tx = (UserTransaction)new InitialContext()
.lookup("java:comp/UserTransaction");
SessionFactory factory = HibernateUtil.getSessionFactory();
PersonDao dao = new PersonDao(factory);
try{
tx.begin();
dao.saveOrUpdate(person);
tx.commit();
} catch( Exception ex) {
tx.rollback();
}
while the only thing that the Dao needs to do is to request the session via: factory.getCurrentSession().<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory name="hibernate/sessionFactory">
<property name="transaction.manager_lookup_class">org.hibernate.transaction.SunONETransactionManagerLookup</property>
<property name="transaction.factory_class">org.hibernate.transaction.JTATransactionFactory</property>
<property name="connection.datasource">jdbc/xaderby</property>
<property name="dialect">org.hibernate.dialect.DerbyDialect</property>
<property name="current_session_context_class">jta</property>
<property name="hbm2ddl.auto">create</property>
<mapping resource="olcc/entity/Person.hbm.xml"/>
</session-factory>
</hibernate-configuration>
where jdbc/xaderby is a data source defined on Glassfish, of type javax.sql.XADataSource.FINEST: saving transient instance FINE: surrounding JTA transaction suspended [JavaEETransactionImpl: txId=1 nonXAResource=null jtsTx=null localTxStatus=0 syncs=[org.hibernate.transaction.CacheSynchronization, org.hibernate.context.JTASessionContext$CleanupSynch@1d069e4]] FINE: opening JDBC connection INFO: JTS5014: Recoverable JTS instance, serverId = [100] FINE: select next_hi from hibernate_unique_key for read only with rs FINE: update hibernate_unique_key set next_hi = ? where next_hi = ? FINE: closing JDBC connection (open PreparedStatements: 0, globally: 0) (open ResultSets: 0, globally: 0) SEVERE: org.hibernate.HibernateException: unable to resume previously suspended transaction at org.hibernate.engine.transaction.Isolater$JtaDelegate.delegateWork(Isolater.java:179) at org.hibernate.engine.transaction.Isolater.doIsolatedWork(Isolater.java:64) at org.hibernate.engine.TransactionHelper.doWorkInNewTransaction(TransactionHelper.java:74) at org.hibernate.id.TableGenerator.generate(TableGenerator.java:118) at org.hibernate.id.TableHiLoGenerator.generate(TableHiLoGenerator.java:84) at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:122)When I tried using a plain javax.sql.DataSource instead of XADataSource, I was getting XAResource.start error.
Person result = Client.create().resource("http://localhost:8080/myapp/person")
.type("application/json")
.post(Person.class,new Person("Jon Smith",133));
Assert.assertEquals("Jon Smith", result.getName()); package olcc.jersey.service;
import java.util.List;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
import olcc.entity.HibernateUtil;
import olcc.entity.Person;
import olcc.entity.PersonCollection;
import org.dozer.DozerBeanMapper;
import org.dozer.Mapper;
import org.hibernate.Transaction;
import org.hibernate.classic.Session;
@Path("/person/")
public class PersonResource {
@GET @Path("{personId}/") @Produces("application/json")
public Person getPerson(@PathParam("personId") int personId) {
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
Transaction tx = null;
Person personClean;
try{
tx = session.beginTransaction();
Person person = (Person) session.load(Person.class,personId);
Mapper mapper = new DozerBeanMapper();
personClean = mapper.map(person,Person.class);
session.getTransaction().commit();
} catch( Exception ex) {
if( tx != null ) tx.rollback();
throw new WebApplicationException(Response.Status.NOT_FOUND);
}
return personClean;
}
@GET @Produces("application/json")
public PersonCollection get() {
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();
List<person> result = session.createQuery("from Person").list();
Mapper mapper = new DozerBeanMapper();
PersonCollection persons = new PersonCollection();
for( Person person : result ) {
persons.add(mapper.map(person,Person.class));
}
session.getTransaction().commit();
return persons;
}
@POST @Consumes("application/json")
@Produces("application/json")
public Person post(Person person) {
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();
session.save(person);
session.getTransaction().commit();
return person;
}
@GET @Path("delete/{personId}/") @Produces("application/json")
public Person deleteOne(@PathParam("personId") int personId) {
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();
Transaction tx = null;
Person personClean;
try{
tx = session.beginTransaction();
Person person = (Person) session.load(Person.class,personId);
session.delete(person);
Mapper mapper = new DozerBeanMapper();
personClean = mapper.map(person,Person.class);
session.getTransaction().commit();
} catch( Exception ex) {
if( tx != null ) tx.rollback();
throw new WebApplicationException(Response.Status.NOT_FOUND);
}
return personClean;
}
}<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory name="hibernate/sessionFactory">
<property name="connection.datasource">jdbc/derbydb</property>
<property name="dialect">org.hibernate.dialect.DerbyDialect</property>
<property name="current_session_context_class">thread</property>
<property name="hbm2ddl.auto">create</property>
<mapping resource="olcc/entity/Person.hbm.xml"/>
</session-factory>
</hibernate-configuration>
private static SessionFactory buildSessionFactory() {
return new Configuration().configure().buildSessionFactory();
}
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.easymock.EasyMock.expect;
import static org.powermock.api.easymock.PowerMock.*;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.legacy.PowerMockRunner;
@RunWith(PowerMockRunner.class)
@PrepareForTest({Person.class })
public class PersonResourceTest {
@Test
public void testPost() {
Person personMock = createMock(Person.class);
expect(personMock.getName()).andReturn("Jon Smith");
replay(personMock);
// do the test...
Assert.assertEquals("Jon Smith", result.name);
}
}
create rule pub_idrule
as @pub_id in ("1389", "0736", "0877", "1622", "1756")
or @pub_id like "99[0-9][0-9]"