Search code examples
javaspringhibernatespring-mvctransactional

@Transactional : Is there a way that hibernate can persist child transacttion when parent fails


I am new in Spring-Hibernate and my issue is : a transaction is not saving the data in the table. But not throwing any exception as well. From the line

Long id = logTransactionFileUpload(fileMetdataBean);

in the function "logClientFile" (in the "Transactional" annotated class/service class, listed below), I am seeing a returned id but data did not show up in the table. .

Finding is : this is nested transaction and was rolled back as the parent had exception from "msgProducer.send( message, jmsConnectInfo )" in JobRunnerServiceImpl class , in submitJob method- means after db insertion. Is there a way that hibernate can persist child transacttion when parent fails?

I am not suspecting my spring/hibernate configuration as save from other parts are working fine. Only this part is the problem.

FYI:

if I turn on ( in the DAO impl class, listed below)

 //getCurrentSession().flush();
//getCurrentSession().getTransaction().commit();

Then data is showing up in the table.But this commit and flush should not be there when @transactional is used.

My @Transactional annotated class:

    @Service("jobRunnerService")
    @Transactional
    public class JobRunnerServiceImpl implements JobRunnerService
    {
        private static final Logger LOG = LoggerFactory.getLogger( MethodHandles.lookup().lookupClass() );



        @Autowired
        @Qualifier("fileLoggerDao")
        private IFileLoggerDAO fileLoggerDAO;

        @Autowired
        @Qualifier("fileMetaDataDao")
        private IGenericDAO<FileMetaDataBean,Long> fileMetaDataDAO;



        public void submitJob(String serviceName, String filePath, long clientId, long layoutId, String audienceId,
                boolean isCA) 
        {

            Map<String, String> parameters = new HashMap<String, String>();

            try
            {
                ..... doing something............

                LOG.info( "Logging file information in FILE_META_DATA table... " );
                String loggedFile = logClientFile( fileName, FACEBOOK_FILE_TYPE, fileExt, clientId, tpList );

               ..... doing something............


                LOG.info( " Submitting job to JMS Q...." );
                msgProducer.send( message, jmsConnectInfo );    

                //test code for the receiver to see if sent messages are received by receiver
                //WildFlyJmsQueueReceive receiver = new WildFlyJmsQueueReceive();
                //receiver.receiveMessagesFromQueue();

            }
            catch ( Exception e )
            {
                String msg = "Error in JobRunnerServiceImpl.submitJob";
                LOG.error(msg,e);
                throw new RuntimeException(msg,e);
            }

        }



        private String logClientFile( String fileName, String fileType, String fileExt, long clientId, List<ToolkitPropertyBean> tpList )        
        {
            ApplicationEnvironment enviro;

            try
            {
                ..... doing something............


                //insert record in FILE_META_DATA table
                FileMetaDataBean fileMetdataBean = new FileMetaDataBean(fileId, new Long(fileTypeID), fileName, fbFilePickUpDir +java.nio.file.FileSystems.getDefault().getSeparator()+ currentFile.getName(), receivedDate,new Long( FileUtilities.getRecordCount( currentFile ) ).longValue(), clientId);
                Long id = logTransactionFileUpload(fileMetdataBean);
                return null;
            }
            catch ( Exception e )
            {
                String msg = "Inside JobRunnerServiceImpl.logClientFile  - Unable to log client file";
                LOG.error(msg,e);
                throw new RuntimeException(msg,e);
            }

        }

        private Long logTransactionFileUpload(FileMetaDataBean bean)
        {
            return (Long)fileMetaDataDAO.save(bean);
        }


    }

My bean :

    @Entity
    @Table(name="FILE_META_DATA", schema = "OAP_META_OWNER", uniqueConstraints = {
            @UniqueConstraint(columnNames = "file_meta_data_id"),
             })
    //@SequenceGenerator(name="file_meta_seq", sequenceName="file_meta_seq")
    public class FileMetaDataBean implements Serializable
    {

        private long fileMetaDataId;
        private Long fileType;
        private String fileName;
        private String originaFileName;
        private Date receivedDt;
        private Long recordCount;
        private Long clientId;

        public FileMetaDataBean(){}

        public FileMetaDataBean( long fileMetaDataId, Long fileType, String fileName, String originaFileName, Date receivedDt,
                long recordCount, long clientId )
        {
            super();
            this.fileMetaDataId = fileMetaDataId;
            this.fileType = fileType;
            this.fileName = fileName;
            this.originaFileName = originaFileName;
            this.receivedDt = receivedDt;
            this.recordCount = recordCount;
            this.clientId = clientId;
        }

        @Id
    //  @GeneratedValue(strategy = GenerationType.AUTO, generator = "file_meta_seq")
        @Column(name = "file_meta_data_id", unique = true, nullable = false)
        public long getFileMetaDataId()
        {
            return fileMetaDataId;
        }

        public void setFileMetaDataId( long fileMetaDataId )
        {
            this.fileMetaDataId = fileMetaDataId;
        }

         @Column(name = "file_type_id", unique = false, nullable = false)
        public Long getFileType()
        {
            return fileType;
        }

        public void setFileType( Long fileType )
        {
            this.fileType = fileType;
        }

        @Column(name = "file_name", unique = false, nullable = false)
        public String getFileName()
        {
            return fileName;
        }

        public void setFileName( String fileName )
        {
            this.fileName = fileName;
        }

        @Column(name = "original_file_name", unique = false, nullable = false)
        public String getOriginaFileName()
        {
            return originaFileName;
        }

        public void setOriginaFileName( String originaFileName )
        {
            this.originaFileName = originaFileName;
        }

        @Column(name = "received_dt", unique = false, nullable = false)
        public Date getReceivedDt()
        {
            return receivedDt;
        }

        public void setReceivedDt( Date receivedDt )
        {
            this.receivedDt = receivedDt;
        }

         @Column(name = "record_count", unique = false, nullable = false)
        public Long getRecordCount()
        {
            return recordCount;
        }

        public void setRecordCount( Long recordCount )
        {
            this.recordCount = recordCount;
        }

        @Column(name = "client_id", unique = false, nullable = false)    
        public Long getClientId()
        {
            return clientId;
        }

        public void setClientId( Long clientId )
        {
            this.clientId = clientId;
        }

    }

The DAO interface

    import java.io.Serializable;
    import java.util.List;
    import java.util.Map;

    import org.hibernate.SessionFactory;
    import org.springframework.transaction.annotation.Transactional;

    public interface IGenericDAO< Entity extends Serializable, ID extends Serializable >
    {

        Entity getById( ID id );

        List<Entity> getAll();

        List<Entity> getAll( String contraints);

        List<Entity> search( Map<String, Object> parms );

        ID save( Entity entity );

        void saveOrUpdate( Entity entity );

        void update( Entity entity );

        void delete( Entity entity );

        void deleteById( ID id );

        void setSessionFactory( SessionFactory sessionFactory );

        void setEntity( final Class clazz );

    }

The DAO impl:

    @SuppressWarnings(value = "unchecked")
    public class GenericDAOImpl<Entity extends Serializable, ID extends Serializable>
            implements IGenericDAO<Entity, ID> {

        protected Class<Entity> clazz;

        public GenericDAOImpl(Class<Entity> clazz) {
            System.out.println(this.getClass().getSimpleName() + " called");
            this.clazz = clazz;
        }

        @Autowired
        @Qualifier("sessionFactory")
        protected SessionFactory sessionFactory;

        @Override
        public Entity getById(ID id) {
            System.out.println("GenericHibernateDAO.getById called with id: " + id);
            return (Entity) getCurrentSession().get(clazz, id);
        }

        @Override
        public List<Entity> getAll() {
            System.out.println("GenericHibernateDAO.getAll called");

            return getCurrentSession().createCriteria(clazz.getName()).list();


    //      return getCurrentSession().createQuery("from " + clazz.getName()).list();
        }

        @Override
        public List<Entity> getAll(String contraints) {
            System.out.println("GenericHibernateDAO.getAll called. Constraint : " + contraints);
            return getCurrentSession().createQuery("from " + clazz.getName() + " " + contraints ).list();
        }

        @Override
        public List search(Map<String, Object> parms) {
            Criteria criteria = getCurrentSession().createCriteria(clazz);

            for (String field : parms.keySet()) {
                criteria.add(Restrictions.ilike(field, parms.get(field)));
            }
            return criteria.list();
        }

        @Override
        public ID save(Entity entity) {
            Serializable id = null;
            try
            {
                id =  getCurrentSession().save(entity);
            }
            catch(RuntimeException e)
            {
                throw e;
            }
    //      getCurrentSession().flush();
    //      getCurrentSession().getTransaction().commit();

            return (ID)id;

        }

        @Override
        public void saveOrUpdate(Entity entity) {
            getCurrentSession().saveOrUpdate(entity);
            getCurrentSession().flush();
            getCurrentSession().getTransaction().commit();
        }

        @Override
        public void update(Entity entity) {
            getCurrentSession().update(entity);
            getCurrentSession().flush();
            getCurrentSession().getTransaction().commit();
        }

        @Override
        public void delete(Entity entity) {
            getCurrentSession().delete(entity);
            getCurrentSession().flush();
            getCurrentSession().getTransaction().commit();
        }

        @Override
        public void deleteById(ID id) {
            delete(getById(id));
        }

        protected Session getCurrentSession() {

            // return sessionFactory.openSession();
            return sessionFactory.getCurrentSession();
        }

        @Override
        public void setSessionFactory(SessionFactory sessionFactory) {
            this.sessionFactory = sessionFactory;
        }

        @Override
        public void setEntity(final Class clazz) {
            this.clazz = clazz;
        }

    }

Solution

  • You can use the

    noRollbackFor

    property of Transactional annotation something like e.g. @Transactional(noRollbackFor=SendMsgFailureException.class). You need to handle the exception of the parent caller method for which you indent not to rollback.