Интеграция Spring + Hibernate: менеджер транзакций не работает с помощью @Transactional

1

Конфигурация Spring выполняется в коде с использованием аннотаций вместо XML файла. Я пытался запросить некоторые данные и создать новые столбцы в базе данных ORACLE через спящий режим. Моя проблема в том, что hibernate генерирует только запросы SELECT, когда я использую sessionFactory.getCurrentSession(). Save(), hibernate не генерирует запросы INSERT. Я думаю, что это может быть проблема с транзакцией, но не может найти, где пошло не так. Я поставлю код ниже, и любая помощь будет оценена по достоинству.

Что основной класс конфигурации:

@Configuration
@ComponentScan
@EnableAutoConfiguration
@EnableTransactionManagement
@PropertySource({ "file:src/main/resources/Config/database.properties" })
public class QCWordImportExportTool {

    @Autowired
    private Environment env;

    @Autowired
    private WietHibernateInterceptor wietHibernateInterceptor;

    /**
     * main, says it all! :)
     * 
     * @param args
     */
    public static void main(String[] args) {
        SpringApplication.run(QCWordImportExportTool.class, args);
    }

    @Bean
    MultipartConfigElement multipartConfigElement() {
        MultiPartConfigFactory factory = new MultiPartConfigFactory();
        factory.setMaxFileSize("10MB");
        factory.setMaxRequestSize("1024KB");
        return factory.createMultipartConfig();
    }

    @Bean
    public LocalSessionFactoryBean sessionFactory() {
        LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
        sessionFactory.setDataSource(restDataSource());
        sessionFactory.setPackagesToScan(new String[] { "com.ciena.prism.almtools.wiet" });
        sessionFactory.setHibernateProperties(hibernateProperties());
        sessionFactory.setEntityInterceptor(this.wietHibernateInterceptor);

        return sessionFactory;
    }

    @Bean
    public DataSource restDataSource() {
        BasicDataSource dataSource = new BasicDataSource();
        dataSource.setDriverClassName(env.getProperty("jdbc.driverClassName"));
        dataSource.setUrl(env.getProperty("jdbc.url"));
        dataSource.setUsername(env.getProperty("jdbc.username"));
        dataSource.setPassword(env.getProperty("jdbc.password"));

        return dataSource;
    }

    @Bean
    public HibernateTransactionManager transactionManager() {
        HibernateTransactionManager transactionManager = new HibernateTransactionManager();
        transactionManager.setSessionFactory(sessionFactory().getObject());

        return transactionManager;
    }

    @Bean
    public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
        return new PersistenceExceptionTranslationPostProcessor();
    }

    Properties hibernateProperties() {
        return new Properties() {
            private static final long serialVersionUID = 1L;

            {
                setProperty("hibernate.dialect", env.getProperty("hibernate.dialect"));
                setProperty("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto"));
                setProperty("hibernate.globally_quoted_identifiers",
                            env.getProperty("hibernate.globally_quoted_identifiers"));
                setProperty("hibernate.show_sql", env.getProperty("hibernate.show_sql"));
                setProperty("hibernate.format_sql", env.getProperty("hibernate.format_sql"));
            }
        };
    }
}

Класс Service - это интерфейс, и я отправлю класс Service Impl основным методом:

@Service
@Transactional(readOnly = true)
public class ImportExportManagerImpl implements ImportExportManager {
    private TestFacade testFacade;
    private TestFolderFacade testFolderFacade;
    private UserManager userManager;

    @Autowired
    SessionFactory sessionFactory;

    @Autowired
    RequirementCoverageDAO requirementCoverageDao;

    @Autowired
    RequirementDAO requirementDao;

    @Autowired
    WietHibernateInterceptor wietHibernateInterceptor;

    @Autowired
    public ImportExportManagerImpl(TestFacade testFacade, TestFolderFacade testFolderFacade,
                                   UserManager userManager) {
        this.testFacade = testFacade;
        this.testFolderFacade = testFolderFacade;
        this.userManager = userManager;
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.ciena.prism.almtools.wiet.managers.ImportExportManager#importTestCases(java.lang.String,
     * java.lang.String, java.util.List)
     */
    @Override
    @Transactional(readOnly = false)
    public void importTestCases(String domain, String project, List<TestCase> testCases)
            throws RequestFailureException, RESTAPIException, InvalidDataException {
        System.out.println("Start to import...");

        setDBSchema(domain, project);

        for (TestCase testCase : testCases) {
            TestFolder testFolder = retrieveTestFolderFromPath(domain, project, testCase.getFolderPath());
            Test test = new Test(testCase, testFolder);
            ALMEntity almEntity = new ALMEntity(test);

            Test existingTest = getExistingTest(domain, project, test);
            if (existingTest == null) {
                existingTest = new Test(testFacade.createEntity(domain, project, almEntity));
            } else {
                testFacade.updateEntity(domain, project, existingTest.getId(), almEntity);
            }
            System.out.println(existingTest.getName());

            /* Create Requirement_Coverage using test and doors_object_ids */
            List<String> doors_object_ids = testCase.getDoors_object_ids();

            for (String doors_object_id : doors_object_ids) {
                List<Requirement> requirementList = requirementDao.findAllFromDoorsobjectid(doors_object_id);

                if (requirementList != null && !requirementList.isEmpty()) {
                    System.out.println("*************Requirement:" + doors_object_id + " not null");
                    /* check if the coverage already exist */
                    Requirement requirement = requirementList.get(0);
                    List<RequirementCoverage> requirementCoverageList = requirementCoverageDao
                            .findAllFromTestIdReqId(Integer.parseInt(existingTest.getId()),
                                                    requirement.getReqId());
                    if (requirementCoverageList == null || requirementCoverageList.isEmpty()) {
                        System.out.println("**************Creating new requirement coverage");

                        /* create a new Requirement Coverage Object */
                        RequirementCoverage requirementCoverage = new RequirementCoverage();
                        requirementCoverage.setRequirement(requirement);
                        requirementCoverage.setEntityId(Integer.parseInt(existingTest.getId()));
                        requirementCoverage.setEntityType("TEST");
                        requirementCoverageDao.create(requirementCoverage);
                        System.out.println("*********assigned DB id: " + requirementCoverage.getId());
                    }

                } else {
                    throw new InvalidDataException("Requirement Management Tool Id : " + doors_object_id
                            + " doesn't exist in QC");
                }
            }
        }

    }
}

И здесь класс DAO impl:

@Repository
public class RequirementCoverageDAOImpl implements RequirementCoverageDAO {

    @Autowired
    private SessionFactory sessionFactory;

    @Override
    public Integer create(RequirementCoverage requirementCoverage) {
        return (Integer) sessionFactory.getCurrentSession().save(requirementCoverage);
    }
}

Тогда класс сущностей:

@Entity
@Table(schema = "wiet", name = "REQ_COVER")
public class RequirementCoverage {

    @SuppressWarnings("unused")
    private static final long serialVersionUID = 1L;

    @Id
    @SequenceGenerator(name = "req_cover_id_gen", sequenceName = "wiet.REQ_COVER_SEQ", allocationSize = 1)
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "req_cover_id_gen")
    @Column(name = "RC_ITEM_ID", unique = true, nullable = false)
    private Integer id;

    @OneToOne
    @JoinColumn(name = "RC_REQ_ID", nullable = false)
    private Requirement requirement;

    @Column(name = "RC_ENTITY_ID", nullable = false)
    private Integer entityId;

    @Column(name = "RC_ENTITY_TYPE", nullable = false)
    private String entityType;

    ....setters and gettters...
}

Надеюсь, я прояснил это и поблагодарил за чтение.

WietHibernateInterceptor используется для динамического изменения схемы:

@Component
public class WietHibernateInterceptor extends EmptyInterceptor {

    private static final long serialVersionUID = 1L;
    private String schema;

    @Override
    public String onPrepareStatement(String sql) {
        String prepedStatement = super.onPrepareStatement(sql);

        if (prepedStatement.toLowerCase().contains("wiet.".toLowerCase())) {
            /* As @SequenceGenerator ignores schema, sequence squery is manually set to be correct */
            prepedStatement = prepedStatement.replaceAll("'", "\"");
            prepedStatement = prepedStatement.replaceAll("wiet.", this.schema + "\".\"");
        }
        /* Change schema dynamically */
        prepedStatement = prepedStatement.replaceAll("wiet", this.schema);
        return prepedStatement;
    }

    public String getSchema() {
        return schema;
    }

    public void setSchema(String schema) {
        this.schema = schema;
    }

}
  • 0
    Вы получаете какие-либо ошибки в консоли?
  • 0
    без ошибок Только без INSERT запросов.
Показать ещё 3 комментария
Теги:
spring
hibernate
transactions

2 ответа

0
Лучший ответ

Hibernate будет генерировать только инструкции INSERT, когда он очищает сеанс. Он очистит сеанс в следующих сценариях

  1. Когда метод @Transactional заканчивается,
  2. Когда он достигает своего предела партии (если он настроен для пакетных вставок) или,
  3. Когда вы вызываете запрос, который он обнаруживает, необходимо очистить сеанс, например, вы вызываете метод count(). Hibernate будет очищать транзакционную сессию и фиксировать ее так, чтобы count() возвращал точное значение записей.

Мои единственные идеи заключаются в том, что создается исключение, которое не попадает (но я бы ожидал, что он будет отображаться в ваших журналах или вашей консоли), что приводит к @Transactional транзакции или что по какой-то причине сеанс @Transactional не создается или не поддерживается.

Первое, что я попробую, удалить диалоги Hibernate из ваших свойств спящего режима. Hibernate делает фантастическую работу, определяющую, какой диалект использовать на основе драйвера, который вы ему даете, и (особенно с драйверами Oracle), если вы заставите его использовать другой диалект, он может вызвать странные ошибки.

2-я вещь, которую я постараюсь, - определить, есть ли SQLException, сгенерированное Hibernate. Я бы предположил, что вы уже включили ведение журнала спящего режима с помощью Log4J или любого используемого вами поставщика ведения журнала. Посмотрите, можете ли вы найти выброшенное SQLException.

  • 0
    Я удалил диалект, и единственное подозрительное, что я нашел в журнале, это: ohhql.internal.ast.ErrorCounter -throwQueryException (): ошибок нет. Я думаю, это означает, что нет ошибок.
  • 0
    Очистка сеанса Hibernate вручную может вызвать запросы INSERT и заставить программу работать, но до сих пор не известно, почему @Transactional не работает.
Показать ещё 1 комментарий
0

В аннотации @Transaction указано readOnly = true. Это означает, что в этой транзакции допускается только чтение. Удалить readOnly = true. Также посмотрите на управление весной транзакции 9.5.6. Использование @Transactional.

  • 0
    Спасибо за ваш ответ, но у меня есть метод @Transactional (readonly = false) для метода импорта, так что это не причина.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню