I am doing tests on an ejb3-project using ejb3unit session bean test. The following test will fail with the last assertNotSame() check.
public void testSave() {
Entity myEntity = new Entity();
myEntity.setName("name1");
myEntity = getBeanToTest().save(myEntity);
assertNotSame("id should be set", 0l, myEntity.getId());
// now the problem itself ...
int count = getBeanToTest().findAll().size();
assertNotSame("should find at least 1 entity", 0, count);
}
So, what is happening. The save(entity) method delivers my "persisted" object with an id set. But when I'll try to find the object using findAll() it won't deliver a single result. How can I get my ServiceBean.save method to work, so the persisted entity can be found?
Edit
My ServiceBean looks like this
#Stateless
#Local(IMyServiceBean.class)
public class MyServiceBean implements IMyServiceBean {
#PersistenceContext(unitName = "appDataBase")
private EntityManager em;
public Entity save(Entity entity) {
em.merge(entity);
}
public List<Entity> findAll() {
... uses Query to find all Entities ..
}
}
and for ejb3unit the ejb3unit.properties:
ejb3unit_jndi.1.isSessionBean=false
ejb3unit_jndi.1.jndiName=project/MyServiceBean/local
ejb3unit_jndi.1.className=de.prj.MyServiceBean
Here we go..
public void testSave() {
Entity myEntity = .. // create some valid Instance
// ...
EntityTransaction tx = this.getEntityManager().getTransaction();
try {
tx.begin();
myEntity = getBeanToTest().save(myEntity);
tx.commit();
} catch (Exception e) {
tx.rollback();
fail("saving failed");
}
// ...
}
maybe this'll help some of you.
Perhaps you don't have a running transaction, hence your entity isn't saved.
One option is to manually start the transaction by injecting a #PersistenceContext in the test, but better look for automatic transaction management in ejb3unit.
Related
Consider this two classes: EmployeeDetailDAOImpl and EmployeeDAOImpl. Assume if I want to create a new employee, I should also create a new record for EmployeeDetail.
Given the below implementation, I wonder if the outer transaction(EmployeeDAOImpl's tx) is rolled back due to any exceptions happened after the detailDAO.create(employeeId) call, will the transaction of new EmployeeDetail be rolled back as well?
public class SessionHandler {
public static getSession() {
return Configuration.buildSessionFactory().openSession(); //ignore the isConnected or other exception handling for now
}
}
public class EmployeeDetailDAOImpl {
public void create(Serializable employeeId) {
Session session = SessionHandler().getSession();
Transaction tx = session.beginTransaction();
try {
EmployeeDetail detail = new EmployeeDetail(employeeId);
session.save(detail );
} catch (Exception e) {
if (tx!= null) {
tx.rollback;
}
}
session.close();
}
}
public class EmployeeDAOImpl {
public void add(String name) {
Session session = SessionHandler().getSession();
Transaction tx = session.beginTransaction();
try {
Employee employee = new Employee(name);
Serializable employeeId= session.save(employee);
EmployeeDetailDAOImpl detailDAO = new EmployeeDetailDAOImpl();
detailDAO.create(employeeId);
//more things here, that may through exceptions.
} catch (Exception e) {
if (tx!= null) {
tx.rollback;
}
}
session.close();
}
}
Actually, none of the given answers is 100% correct.
It depends on the calling party/service.
If you are calling the methods from an EJB, you will have 1 transaction covering both method calls. That way, the transaction will roll back both operations in case of an exception. Reason behind this is that every method in EJB is transaction Required, unless specified otherwise in the annotation or ejb deployment descriptor.
If you are using spring or any other DI framework, then it depends on your configuration. In a normal setup, your calling transaction will be suspended, since the JPA EJB will create its own transaction. You can however use the JTATransactionManager (As specified here) to make sure that both your EJB and your Spring bean share the same transaction.
If you call the JPA methods from a POJO, then you will have to take care of the JTA transaction handling yourself.
Yes, it will rollback the entity Employee as well. It doesn't even depend on whether the other entities are related.
It depends on the scope of the transaction, which here includes both Employee and EmployeeDetails
You are creating two different transaction for each method.Hence rollback can not happen.
To rollback the transaction you require the propogation in Transaction.
You need to write the code like below::
#Transactional(propagation=Propagation.REQUIRED)
public void testRequired(User user) {
testDAO.insertUser(user);
try{
innerBean.testRequired();
} catch(RuntimeException e){
// handle exception
}
}
Below is link for more information of Propogation.
http://docs.spring.io/spring-framework/docs/2.5.6/api/org/springframework/transaction/annotation/Propagation.html
http://www.byteslounge.com/tutorials/spring-transaction-propagation-tutorial
i'm studying JTA and i tried make a find example to test my concept:
First a created a class with CDI injeting my DAO:
#Named
#RequestScoped
public class SegurancaServiceJTA {
#Inject
private DAOSeguranca daoSeguranca;
#Transactional(value = Transactional.TxType.NEVER)
public void findTest(){
List result = daoSeguranca.findList("SELECT u FROM Usuario u",null);
}
}
Inside my DAO i'm injeting my EntityManager with #PersistenceContext annotation.
public class DAOSeguranca implements IDAO, Serializable {
#PersistenceContext(unitName = "seguranca")
private EntityManager em;
#SuppressWarnings({"unchecked", "rawtypes"})
public List findList(String hql, Map<String, Object> namedParams, Integer first,
Integer maxResult, Class qualifier, boolean buscarTodos) {
try {
logger.debug("Executando findList. HQL = " + hql);
Query query = em.createQuery(hql);
configureParams(namedParams, query);
if (first != null) {
query.setFirstResult(first);
}
if (maxResult != null) {
query.setMaxResults(maxResult);
} else if (!buscarTodos) {
query.setMaxResults(100);
}
return query.getResultList();
} catch (Exception e) {
logger.error(e);
throw new DAOException(e.getMessage());
}
}
}
My doubt are:
1) I understand that this method should not work because entityManager can find anything without a transaction, and i'm using TxType.NEVER to force error. But this method works normal and return the beans.
2) In my persistence.xml i'm using JTA, but if i put RESOURCE_LOCAL everything continue working. So, this is right ? I understand that JTA should work only with JTA in persistence.xml and not RESOURCE_LOCAL.
I have a class, Student and the generated Endpoint class for it. ListStudents and insertStudents methods work without any problems, but update and remove don't cause any change in the datastore. The methods don't throw any errors and the call returns, but no changes are made.
My endpoints code is mostly the code generated by google plugin for eclipse:
#ApiMethod(name = "removeStudent", path="remove_student")
public void removeStudent(#Named("email") String email) {
EntityManager mgr = getEntityManager();
try {
Student student = getStudentByEmailName(email);
mgr.remove(student);
} finally {
mgr.close();
}
}
Entitiy manager getter method:
private static EntityManager getEntityManager() {
return EMF.get().createEntityManager();
}
#ApiMethod(name = "updateStudent")
public Student updateStudent(Student student) {
EntityManager mgr = getEntityManager();
try {
if (!containsStudent(student)) {
throw new EntityNotFoundException("Object does not exist");
}
mgr.persist(student);
} finally {
mgr.close();
}
return student;
}
And my EMF class:
public final class EMF {
private static final EntityManagerFactory emfInstance = Persistence
.createEntityManagerFactory("transactions-optional");
private EMF() {
}
public static EntityManagerFactory get() {
return emfInstance;
}
}
The client that uses this endpoint is Android. I have only tried testing on my local server.
Please tell me if I'm doing something wrong. Thank you
Do you have your student entities indexed by email?
This is a typical issue when you move to nosql and expect all queries to work without indexes.
Note that records inserted before defining index would not be in index.
The datastore is eventually consistent and your code should work. What is the return value that you get in the Student object from your updateStudent method.
As much as I don't want to, after you do a mgr.persist(...) , add mgr.flush() and see if that makes a difference.
simple example, i have a user with a unique name in the database. Now i add a user with same name as an existing user. I get a very big exception- stacktrace for it. Can i reduce this output with a parameter from openjpa? I tried some of this, but nothing works: http://openjpa.apache.org/builds/1.0.1/apache-openjpa-1.0.1/docs/manual/ref_guide_logging.html
The other solution is to check the name before persisting, but i think thats an overhead or?
#ViewScoped
#ManagedBean
public class MyBean {
#EJB
private MyStateless stateless;
public void save(ActionEvent event){
try{
stateless.save();
}catch(Exception e){
System.out.println("####EXCEPTIOn");
}
}
}
#Stateless
#LocalBean
public class MyStateless{
#PersistenceContext(unitName = "pu")
private EntityManager em;
public void save() {
Person p = new Person(1, "Name");
em.persist(p);
}
}
org.apache.openjpa.persistence.EntityExistsException: The transaction has been rolled back. See the nested exceptions for details on the errors that occurred.
FailedObject: myejb.Person#1100245.......
2014-03-02T10:43:47.969+0100|Information: ####EXCEPTIOn
Here i get the javax.ejb.EJBException with the wrapped EntityExistsException
I am using Servlet->EJB->JPA on Glassfish 4. Application deploys successfully.
When I run the servlet, it updates the entity with id=1 in db, but doesn't do anything for the one with id=2. No exception is thrown.
#WebServlet("/AnimalServlet")
public class AnimalServlet extends HttpServlet {
#EJB AnimalDAOLocal lOBAnimalDAO;
protected void doGet(....) {
Animal lOBAnimal = lOBAnimalDAO.getAnimal(1); // gets OK
lOBAnimal.setName("Animal1"); // sets OK
lOBAnimalDAO.mergeAnimal(lOBAnimal); // updates in DB OK
lOBAnimal = lOBAnimalDAO.getAnimal(2); // gets OK
lOBAnimal.setName("Animal2"); // sets OK
lOBAnimalDAO.mergeAnimal(lOBAnimal); // doesn't update in DB.
}
And Session Bean Methods are :
#Stateless(mappedName = "AnimalDAOMapped")
public class AnimalDAO implements AnimalDAOLocal {
#PersistenceContext EntityManager em;
public Animal getAnimal(int id) {
return em.find(Animal.class, id);
}
public void mergeAnimal(Animal pOBAnimal) {
em.merge(pOBAnimal);
}
}
Persistence Unit Settings :
<persistence-unit name="JPATest" transaction-type="JTA">
<jta-data-source>jdbc/animaltest</jta-data-source>
<class>net.test.model.Animal</class>
</persistence-unit>
Use flush(); method, throw PersistenceException.Servlet catch that exception.
public void mergeAnimal(Animal pOBAnimal) throws PersistenceException {
try {
em.merge(pOBAnimal);
em.flush();
} catch (PersistenceException pe) {
throw e;
}
}
I solved the problem with the help of Chris' suggestion to turn on EclipseLink Logging ( Which I didn't know before ) .
The problem happened because of wrong #JoinColumn definition in the entity. And the first entity ( db=1 ) was committing by chance because of the existance of dummy data for this id which doesn't exist for other ids.