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.
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 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 having a problem related to JPA & some hibernate listeners I configured to index/deindex the Db entities into Elastic Search. The problem is basically that the listener onPostInsert method is called even if I throw an exception in the method where I am persisting an entity and this method is marked as #Transactional(rollbackFor = {Throwable.class}). My configuration is as follows.
The listener class:
public class ElasticSearchEventListener implements PostDeleteEventListener,
PostInsertEventListener, PostUpdateEventListener {
#Override
public void onPostInsert(PostInsertEvent event) {
log.debug("Listener indexing entity");
try {
updateElasticSearch(event.getEntity());
} catch (Exception e) {
log.debug("Error indexing object from listener");
e.printStackTrace();
}
}
.......
}
The listener configured class:
#Service #Log4j
public class ListenerConfigurerImpl implements ListenerConfigurer {
#Autowired
private EntityManagerFactory entityManagerFactory;
#Autowired
private ElasticSearchEventListener listener;
#PostConstruct #Override
public void registerListeners() {
log.debug("Registering event listeners");
HibernateEntityManagerFactory hibernateEntityManagerFactory = (HibernateEntityManagerFactory) this.entityManagerFactory;
SessionFactoryImpl sessionFactoryImpl = (SessionFactoryImpl) hibernateEntityManagerFactory.getSessionFactory();
EventListenerRegistry registry = sessionFactoryImpl.getServiceRegistry().getService(EventListenerRegistry.class);
registry.getEventListenerGroup(EventType.POST_COMMIT_INSERT).appendListener(listener);
.......
}
}
A service class:
#Service #Log4j
public class ConversationServiceImpl implements ConversationService {
#Override
#Transactional(rollbackFor = {Throwable.class})
public void quotePackage(Long userId, CustomQuoteDTO dto) {
......
Conversation conversation = Conversation.createAndAssign(user, agency, type, subject);
conversation = conversationRepository.save(conversation);
Long conversationId = conversation.getId();
if (1 == 1) throw new RuntimeException();
}
}
Based on this configuration, I would be expecting that the conversation entity is not saved neither in the DB nor Elastic Search. The entity is not persisted in the DB which is correct but for some reason the "onPostInsert" is still executing... and I get the entity in Elastic Search even if it is not in the Database.
Any ideas? I am a bit lost.
Thanks in advance.
EDIT 1 ------
I have found this bug from 2006 and it is still open that seems to be my problem: https://hibernate.atlassian.net/browse/HHH-1582
Is this supposed to work this way?
The pull request added here https://hibernate.atlassian.net/browse/HHH-1582 fixes this issue.
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.