Lost transaction with JPA unique constraint? - java

I have a field with an unique constraint:
#Column(unique=true)
private String uriTitle;
When I try to save two entities with the same value, I get an exception - but another exception than I exected:
java.lang.IllegalStateException: <|Exception Description: No transaction is currently active
at org.eclipse.persistence.internal.jpa.transaction.EntityTransactionImpl.rollback(EntityTransactionImpl.java:122)
at com.example.persistence.TransactionFilter.doFilter(TransactionFilter.java:35)
The questionable filter method looks like this:
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain)
throws IOException, ServletException {
EntityManager entityManager = ThreadLocalEntityManager.get();
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
try {
chain.doFilter(request, response);
transaction.commit();
}
catch (Throwable t) {
transaction.rollback(); // line 35, mentioned in the exception
throw new RuntimeException(t);
}
finally {
entityManager.close();
ThreadLocalEntityManager.reset();
}
}
... and the ThreadLocalEntityManager like this:
public class ThreadLocalEntityManager {
private static EntityManagerFactory entityManagerFactory = null;
private static final ThreadLocal<EntityManager> entityManager =
new ThreadLocal<EntityManager>() {
#Override
protected EntityManager initialValue() {
return entityManagerFactory.createEntityManager();
}
};
private ThreadLocalEntityManager() {}
public static synchronized void init(String persistenceUnit) {
requireNotNull(persistenceUnit);
requireState(entityManagerFactory == null, "'init' can be called only "
+ "once.");
entityManagerFactory =
Persistence.createEntityManagerFactory(persistenceUnit);
}
public static EntityManager get() {
requireState(entityManagerFactory != null, "Call 'init' before calling "
+ "'get'");
return entityManager.get();
}
public static void reset() {
entityManager.remove();
}
}
I wrapped the call to save with try ...catch to handle the unique constraint violation, but that doesn't work:
try {
ThreadLocalEntityManager.get().persist(article); // article is constrained
}
catch(Throwable t) {
t.printStackTrace();
}
Any idea why there is no transaction? What is the correct way to handle a unique constraint violation?

Your transaction rolled back automatically due to DB constraint violation, therefore there is no transaction to rollback manually. To handle this case, you can write
catch (Throwable t) {
if (transaction.isActive())
transaction.rollback(); // line 35, mentioned in the exception
throw new RuntimeException(t);
}

You don't have any separation between transactions and GUI in your application. You will have problems with transaction handling in general.

Related

How to catch already caught exception?

I have the follow the following filter:
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
ServletException {
try {
chain.doFilter(new XSSRequestWrapper((HttpServletRequest) request), response);
} catch (XssAttackException e) {
request.getRequestDispatcher("/XssAttack").forward(request, response);
}
}
and the class XssAttackException is:
public class XssAttackException extends RuntimeException {
private static final long serialVersionUID = 1L;
}
after debugging the code, I realized that somewhere in the spring framework all the exceptions are being caught. Now I need a way that my catch bock also run.
UPDATE
inside XSSRequestWrapper we have:
#Override
public String getHeader(String name) {
String value = super.getHeader(name);
return stripXSS(value);
}
And
private String stripXSS(String value) {
if (value != null) {
value = persianUtf8(value);
if (!value.equals(Jsoup.parse(value).text())) {
throw new XssAttackException();
}
value = Jsoup.parse(value).text();
for (Pattern scriptPattern : patterns) {
if (scriptPattern.matcher(value).matches()) {
throw new XssAttackException();
}
value = scriptPattern.matcher(value).replaceAll("");
}
}
return value;
}
Please don't assume this is answer for your question.Assumed too long comment.
I created my CustomException class.
public class CustomException extends RuntimeException {
}
and created custom Servlet class as your XSSRequestWrapper and throw my custom exception in constructor.
public class MyServlet implements ServletRequest {
public MyServlet() {
throw new CustomException();
}
// other override methods go here
}
and in my filter class
try {
chain.doFilter(new MyServlet(), response);
} catch (CustomException e) {
System.out.println("xxxxxxxxxxxxxxx I got it xxxxxxxxxxxxxxxxxxx");
}
This code work fine. At your program , I think there has some exception has occured and you did not catch on them. So , this exception object has miss from your try block of your filter class and handled by Spring container.

Hibernate SessionFactory and could not obtain transaction-synchronized session for current thread

I know this question has been asked before however none of the solutions have worked for me.
I am trying to hit a controller to populate an index. The issue arises when I try and search the database for updates.
Here is are the classes I am dealing with:
Configuration:
#Configuration
#EnableTransactionManagement
public class WebApplication implements WebApplicationContextInitializer, ApplicationContextAware {
#Bean(name="dataSource")
public DataSource getDataSource() throws IOException {
InitialContext initialContext = new Context();
return (DataSource) initialContext.lookup("java:comp/env/jdbc/myDataSource");
}
#Bean(name="sessionFactory")
public SessionFactory getSessionFactory() throws IOException {
LocalSessionFactoryBuilder sessionBuilder = new LocalSessionFactoryBuilder(getDataSource());
sessionBuilder.scanPackages(PropertyUtil.getInstance().getPropertySplitTrimmed("hibernate", "packagesToScan"));
sessionBuilder.addProperties(PropertyUtil.getInstance().getProperties("hibernate"));
return sessionBuilder.buildSessionFactory();
}
#Bean(name="transactionManager")
public HibernateTransactionManager transactionManager() throws IOException {
return new HibernateTransactionManager(getSessionFactory());
}
}
Controller:
#RestController
#Transactional
#RequestMapping("/persons")
public class IndexController {
#Autowired
PersonsDao personsDoa;
private ExecutorService executorService = Executors.newFixedThreadPool(100);
#RequestMapping(value="/index")
public void populateIndex(#DefaultValue("") #RequestParam String name){
...
...
List<Future<Persons>> holder = new ArrayList<>();
for(Persons p : people){
String name = p.name();
Future<Person> f = this.executorService.submit(new Callable<Person>(){
#Override
public Person call() throws Exception {
return personsDao.findByName(name); // <-- Throws error here
}
});
holder.add(f); // process the array later once all threads are finished
}
...
...
}
}
UPDATE: I've updated my Controller according to some suggestions, however I am still receiving the same error
Controller:
#RestController
#Transactional
#RequestMapping("/persons")
public class IndexController {
#Autowired
PersonsDao personsDoa;
private ExecutorService executorService = Executors.newFixedThreadPool(100);
#RequestMapping(value="/index")
public void populateIndex(#DefaultValue("") #RequestParam String name){
...
...
List<Future<Persons>> holder = new ArrayList<>();
TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(sessionFactory.getCurrentSession())); //<-- THROWS ERROR HERE
for(Persons p : people){
String name = p.name();
Future<Person> f = this.executorService.submit(new Callable<Person>(){
SessionHolder holder = (SessionHolder)TransactionSynchronizationManager.getResources(sessionFactory);
Session session = holder.getSession();
#Override
public Person call() throws Exception {
Transaction t = session.getTransaction();
t.begin();
Persons p = personsDao.findByName(name);
t.commit();
session.flush();
return p;
}
});
holder.add(f); // process the array later once all threads are finished
}
...
...
}
}
Usualy the request thread use only one shared Session, this session is binded at the start of the request, and unbinded at the end of the request, but given you want to use it in another thread, we must :
1_ prevent the session from being closed from the Request Thread.
2_ bind this session to the new thread, to offer the TransactionManager to works with same Session.
First the current Session must not be closed, so if you are using the OpenInViewFilter, you need to add a method before calling the new Thread.
OpenEntityManagerInViewFilter.getCurrent().keepEmfOpen(request);
Then inside the Thread you need to attach the current session.
public void attachThread() {// this must bind the session to this thread.
OpenEntityManagerInViewFilter.getCurrent().registerEmfs(request, session);
}
private boolean registerEmf(String key, ServletRequest request, EntityManagerFactory emf){
if (emf == null)
return true;
if (TransactionSynchronizationManager.hasResource(emf))
return true;
else {
boolean isFirstRequest = true;
WebAsyncManager asyncManager = null;
if (request!=null){
asyncManager = WebAsyncUtils.getAsyncManager(request);
isFirstRequest = !(request instanceof HttpServletRequest) || !isAsyncDispatch((HttpServletRequest) request);
}
if (emf.isOpen())
if (isFirstRequest || !applyEntityManagerBindingInterceptor(asyncManager, key)) {
logger.debug("Opening JPA EntityManager in OpenEntityManagerInViewFilter");
try {
EntityManager em = createEntityManager( emf );
EntityManagerHolder emHolder = new EntityManagerHolder( em );
TransactionSynchronizationManager.bindResource( emf, emHolder );
if (asyncManager!=null)
asyncManager.registerCallableInterceptor( key, new EntityManagerBindingCallableInterceptor( emf, emHolder ) );
return false;
}
catch (PersistenceException ex) {
throw new DataAccessResourceFailureException("Could not create JPA EntityManager", ex);
}
}
}
return true;
}
in case of OpenSessionInViewFilter :
private void openHibernateSessionInView(){
Session session=SessionFactoryUtils.getSession(sessionFactory,true);
SessionHolder holder=new SessionHolder(session);
if (!TransactionSynchronizationManager.hasResource(sessionFactory)) {
TransactionSynchronizationManager.bindResource(sessionFactory,holder);
}
}
private void closeHibernateSessionInView(){
if (TransactionSynchronizationManager.hasResource(sessionFactory)) {
SessionHolder sessionHolder=(SessionHolder)TransactionSynchronizationManager.unbindResource(sessionFactory);
if (sessionHolder.getTransaction() != null && !sessionHolder.getTransaction().wasRolledBack() && !sessionHolder.getTransaction().wasCommitted()) {
sessionHolder.getTransaction().commit();
}
SessionFactoryUtils.closeSession(sessionHolder.getSession());
}
}

How to make DAO thread safe JPA?

I am new to JPA and Java. I understood how EntityManager and EMF works a littel bit. I have JPAUtill from where I get EntityManager, which is using ThreadLocal variable, which makes EntityManager thread safe. I have DAO where in I am persisting new users when having 10 thousand thread accesses simultaneously.
I get to see following exception:
javax.persistence.PersistenceException: org.hibernate.SessionException: Session is closed!
at org.hibernate.ejb.AbstractEntityManagerImpl.throwPersistenceException(AbstractEntityManagerImpl.java:614)
Here is my DAO code
try{
entityManager = JPAUtil.getEntityManager();
user.setArea(getArea());
user.setCity(getCity());
user.setPassword("xxxxx");
user.setEmail_Id(getEmail_Id());
user.setMobile_Num(TempMobile_Num);
user.setFirst_Name(getFirst_Name());
user.setSession_Id("0");
user.setEmail_Verification_Status("NOT VERIFIED");
user.setBalance(new BigDecimal(0.00));
if (!entityManager.getTransaction().isActive()){
entityManager.getTransaction().begin();
}
entityManager.persist(user);
JPAUtil.commit();
} catch (Exception e) {
logger.info(e);
e.printStackTrace();
} finally{
if(entityManager.isOpen())
JPAUtil.closeEntityManager();
}
JPAUtil:
public class JPAUtil {
private static Logger logger = Logger.getLogger(JPAUtil.class);
private static EntityManager entityManager = null;
private static final ThreadLocal<EntityManager> threadLocal;
private static EntityManagerFactory emf = null;
static {
try {
emf = Persistence.createEntityManagerFactory("XYZDB");
threadLocal = new ThreadLocal<EntityManager>();
} catch (Throwable ex) {
logger.info("JPAUtil Exception", ex);
throw new ExceptionInInitializerError(ex);
}
}
public static EntityManager getEntityManager() {
try {
entityManager = threadLocal.get();
if (entityManager == null || !entityManager.isOpen()) {
JPAUtil.entityManager = emf.createEntityManager();
threadLocal.set(entityManager);
} else if (!emf.isOpen()) {
entityManager = threadLocal.get();
emf = Persistence.createEntityManagerFactory("XYZDB");
JPAUtil.entityManager = emf.createEntityManager();
threadLocal.set(entityManager);
}
} catch (Throwable ex) {
logger.info("JPAUtil Exception", ex);
throw new ExceptionInInitializerError(ex);
}
return JPAUtil.entityManager;
}
public static void closeEntityManager() {
EntityManager entityManager = threadLocal.get();
if (entityManager != null) {
entityManager.close();
threadLocal.set(null);
}
}
public static void closeEntityManagerFactory() {
emf.close();
}
public static void commit() {
if (!entityManager.getTransaction().isActive()) {
entityManager.getTransaction().begin();
}
entityManager.getTransaction().commit();
}
public static void rollback() {
if (!entityManager.getTransaction().isActive()) {
entityManager.getTransaction().begin();
}
entityManager.getTransaction().rollback();
}
}
Can someone please help me understand the issue and how to resolve it?
Remove this static variable form JPAUtil:
private static EntityManager entityManager = null;
You were still using a static variable; you didn't really use the ThreadLocal.
Change your getEntityManager to this:
public static EntityManager getEntityManager() {
EntityManager entityManager;
try {
entityManager = threadLocal.get();
if (entityManager == null || !entityManager.isOpen()) {
entityManager = emf.createEntityManager();
threadLocal.set(entityManager);
}
} catch (Throwable ex) {
logger.info("JPAUtil Exception", ex);
throw new ExceptionInInitializerError(ex);
}
return entityManager;
}

EntityManager.close() Not Closing The Connections to Database Tomcat

Here is my JPA Utill Class....
private static Logger logger=Logger.getLogger(JPAUtil.class);
private static EntityManager entityManager = null;
private static final ThreadLocal<EntityManager> threadLocal;
private static EntityManagerFactory emf = null;
static
{
try {
emf = Persistence.createEntityManagerFactory("XYZ");
threadLocal = new ThreadLocal<EntityManager>();
} catch (Throwable ex) {
logger.info("JPAUtil Exception",ex);
throw new ExceptionInInitializerError(ex);
}
}
public static EntityManager getEntityManager() {
try {
entityManager = threadLocal.get();
if (entityManager == null || !entityManager.isOpen()) {
JPAUtil.entityManager = emf.createEntityManager();
threadLocal.set(entityManager);
}else if(!emf.isOpen())
{
entityManager = threadLocal.get();
emf = Persistence.createEntityManagerFactory("XYZ");
JPAUtil.entityManager = emf.createEntityManager();
threadLocal.set(entityManager);
}
} catch (Throwable ex) {
logger.info("JPAUtil Exception",ex);
throw new ExceptionInInitializerError(ex);
}
return JPAUtil.entityManager;
}
public static void closeEntityManager() {
EntityManager entityManager = threadLocal.get();
if (entityManager != null) {
entityManager.close();
threadLocal.set(null);
}
}
public static void closeEntityManagerFactory() {
emf.close();
}
public static void commit() {
if (!entityManager.getTransaction().isActive()){
entityManager.getTransaction().begin();
}
entityManager.getTransaction().commit();
}
public static void rollback() {
if (!entityManager.getTransaction().isActive()){
entityManager.getTransaction().begin();
}
entityManager.getTransaction().rollback();
}
And After each and every transactions i am closing entitymanager....with
finally block...and later i came to Know that even in select query we need to
begin transaction and commit it...so i did try that in my application Home
page Just executing 2 select query and in the server mysql is Occupying the memory the most....with the eclipse MAT i Anallise the Memory Leak....
As per my understanding since i am using tomcat i cant do connection pooling..and we will be moving to jboss...but till the i would like to patch this....any help would be appreciated :) :)

How to remove stateless entity Java persistence

I have the following situation with Java persistence:
public ReturnCodes startWork() {
sessionBackup = (BaseService<Backup>) ctx.lookup("XYZ/BackupServiceImpl/local");
Backup backup = new Backup();
backup.setStatus(BackupStatus.EXECUTING);
....
sessionBackup.save(Backup) //at this point is not actualy saved into DB
....//try to connect somewhere
if ( !ConnectSuccess) {
sessionBackup.remove(backup);
return ReturnCodes.REQUESTABORT
}
....
}
#Stateless
public class BackupServiceImpl extends BaseServiceImpl<Backup> implements
BaseService<Backup>
{
#Override
protected Class<Backup> getEntityClass()
{
return Backup.class;
}
}
And the save and remove methods of BaseServiceImpl:
public abstract class BaseServiceImpl<T extends Serializable> implements
BaseService<T>
{
protected EntityManagerFactory emf;
public T save(T entity)
{
EntityManager em = emf.createEntityManager();
em.persist(entity);
return entity;
}
public void remove(T entity)
{
EntityManager em = emf.createEntityManager();
try
{
final Method method = getEntityClass().getMethod("getId");
final String id = (String) ((T) method.invoke(entity));
entity = em.find(getEntityClass(), id);
em.remove(entity);
}
catch (final Exception ex)
{
logger.log(Level.WARNING, "Unexpected error", ex);
}
}
}
I don't want to save into the DB in case ConnectSuccess fails, but the remove method fails to find the entity (because is not yet into the DB), and after returning ReturnCodes.REQUESTABORT the entity is saved.
How can I cancel the save?
In general in this case you do a setRollbackOnly().
You may also throw an exception which will trigger the rollbackonly.
See http://www.developerscrappad.com/547/java/java-ee/ejb3-x-jpa-when-to-use-rollback-and-setrollbackonly/
By the way doing this in your code:
catch (final Exception ex)
{
logger.log(Level.WARNING, "Unexpected error", ex);
}
is too broad and will block rollback functionality.

Categories