@Transactional: есть ли способ, которым спящий режим может сохранять дочернюю транзакцию, когда родитель терпит неудачу?

Я новичок в Spring-Hibernate, и моя проблема: транзакция не сохраняет данные в таблице. Но и не генерировать никаких исключений. Из строки

Long id = logTransactionFileUpload(fileMetdataBean);

в функции "logClientFile" (в аннотированном классе/классе службы "Transactional", указанном ниже), я вижу возвращаемый идентификатор, но данные не отображаются в таблице. .

Находка: это вложенная транзакция, и она была отброшена, поскольку у родителя было исключение из "msgProducer.send(сообщение, jmsConnectInfo)" в классе JobRunnerServiceImpl, в методе submitJob - означает после вставки базы данных. Есть ли способ, которым спящий режим может сохранять дочернюю транзакцию, когда родитель терпит неудачу?

Я не подозреваю, что моя конфигурация spring/hibernate работает нормально. Только в этой части проблема.

К вашему сведению:

если я включу (в классе реализации DAO, указанном ниже)

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

Затем данные отображаются в таблице. Но этой фиксации и сброса не должно быть, когда используется @transactional.

Мой аннотированный класс @Transactional:

    @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);
        }


    }

Мой боб:

    @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;
        }

    }

Интерфейс DAO

    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 );

    }

ДАО подразумевает:

    @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;
        }

    }

person VictorGram    schedule 13.09.2016    source источник
comment
Пробовали ли вы перемещать @Transactional с уровня класса на уровень метода? Ожидаете ли вы, что когда выполнение logClientFile завершится, должна быть запись в БД (видимая) до завершения метода submitJob? Я также столкнулся с той же проблемой, когда пытался выполнить длинную задачу в одной транзакции. Вы можете получить id, потому что запись вставлена ​​в БД, но, поскольку ваша транзакция все еще выполняется, вы не можете увидеть запись в БД. Вы добавили @Transactional в класс, это означает, что все методы в этом классе находятся в транзакции. Проверьте БД после завершения выполнения вашей программы.   -  person OO7    schedule 14.09.2016
comment
Вы правы. Это вложенная транзакция, и она была отброшена, поскольку у родителя было исключение из msgProducer.send(сообщение, jmsConnectInfo) в классе JobRunnerServiceImpl, в методе submitJob - означает после вставки БД. Следовательно, откат. Было интересно, есть ли способ сохранить дочернюю транзакцию, даже если родитель терпит неудачу.   -  person VictorGram    schedule 14.09.2016
comment
Добавьте аннотацию @Transactional на уровень метода и попробуйте с propagation.   -  person OO7    schedule 15.09.2016


Ответы (1)


Вы можете использовать

noRollbackFor

свойство аннотации Transactional что-то вроде, например. @Transactional(noRollbackFor=SendMsgFailureException.class). Вам необходимо обработать исключение метода родительского вызывающего объекта, для которого вы не делаете отступ для отката.

person mhasan    schedule 14.09.2016
comment
пробовал с @Transactional(noRollbackFor=FileMetaDataBean.class) на уровне класса для класса JobRunnerServiceImpl. Это не работает. - person VictorGram; 15.09.2016
comment
для вашего случая вы должны поместить @Transactional на уровень метода, так как ваш метод будет вызывать исключение, для которого вы не хотели бы откатывать. Также noRollbackFor всегда должен быть для класса исключения, а не для вашего класса обслуживания - person mhasan; 15.09.2016
comment
Спасибо, мхасан. Он работал после добавления @Transactional(noRollbackFor=RuntimeException.class) на уровне метода, где он получал исключение времени выполнения. - person VictorGram; 15.09.2016