Все вложенные транзакции JPA фиксируют / откатывают после исключения времени выполнения - PullRequest
0 голосов
/ 14 января 2019

Через некоторое время, прочитав и протестировав различные формы загрузки экземпляров и компонентов EntityManager для выполнения некоторых вложенных транзакций, наконец, я не могу найти способ, чтобы после исключения времени выполнения данные делали полный откат, что должно быть близко к стадии Я хочу, чтобы в БД сохранялась только одна сущность. Ситуация следующая: У меня есть одна сущность класса Car и одна сущность класса CarPart, один экземпляр Car может содержать список CarPart, который я протестировал на предмет сохранения, и когда не возникает ошибка времени выполнения, все работает как чудо. Проблема заключается в том, что, когда БД выдает ошибку времени выполнения, как контроллер ошибки триггера, иногда объекты частично сохраняются. Я использую JPA с JavaEE и EclipseLink. Мне нужен способ контролировать, что объект будет сохраняться полностью или выполнять откат каждого постоянного действия. Любая помощь будет оценена.

Ответы [ 2 ]

0 голосов
/ 15 января 2019

Наконец, после некоторого времени тестирования, я прихожу к одному решению, которое подходит для моего сценария. Я публикую не весь код, а только основные части, соответствующие решению проблемы:

Менеджерский класс

@ManagedBean
@ApplicationScoped
@TransactionManagement(TransactionManagementType.BEAN)
public class CarsManager implements Serializable {
    @Resource
    private UserTransaction userTransaction;
    private CarsRepositorySameEM carsRepoSameEM;
    private CarsPartsRepositorySameEM carsPartsRepoSameEM;
    private EntityManager entityManager;
    private Cars car;
    private List<CarsParts> carPartList;

    @Inject
    public CarsManager(@MainEM EntityManager em) {
        this.entityManager = em;
        this.carsRepoSameEM = new CarsRepositorySameEM(this.entityManager);
        this.carsPartsRepoSameEM = new CarsPartsRepositorySameEM(this.entityManager);
    }

    public Cars getCar() {
        return car;
    }

    public void setCar(Cars car) {
        this.car = car;
    }

    public List<CarsParts> getCarPartList() {
        return carPartList;
    }

    public void setCarPartList(List<CarsParts> carPartList) {
       this.carPartList = carPartList;
    }

    public Boolean save() {
        Boolean result = Boolean.TRUE;
        try {
            this.userTransaction.begin();
            this.car = carsRepoSameEM.saveCar(this.car);
            if(carsPartsRepoSameEM.getEntityException().getCode() != 0) {
               this.userTransaction.setRollbackOnly();
            }
            for (int i = 0; i < carPartList.size(); i++) {
                carPartList.get(i).setCarsId(car.getID());
            }
            this.carPartList = carsPartsRepoSameEM.saveParts(carPartList);
            if(carsPartsRepoSameEM.getEntityException().getCode() != 0) {
                this.userTransaction.setRollbackOnly();
            }
            car.setCarsPartsList(this.carPartList);
            this.userTransaction.commit();
        } catch (Exception e) {
            //e.printStackTrace();
            result = Boolean.FALSE;
            try {
                this.userTransaction.rollback();
            } catch (Exception ex) {
            }
        }
        return result;
    }
}

Хранилища классов:

@Stateless
public class CarsRepositorySameEM extends AbstractRepository<Cars, Long> {
    ...
    public Cars saveCar(Cars car) throws SystemException {
        try {
            this.entityManager.joinTransaction();
            car = this.create(car);
            this.entityManager.flush();
        } catch (Exception e) {
            this.setEntityException(GenericExceptionType.EX_JPA, -1, "CarsRepositorySameEM", "saveCar()", "Error al persistir el Car", null, e.getMessage());
            //throw e;
        }
        return car;
    }
}

-

@Stateless
public class CarsPartsRepositorySameEM extends AbstractRepository<CarsParts, Long> {
    ...
    public List<CarsParts> saveParts(List<CarsParts> carsPartsList) throws SystemException {
        try {
            this.entityManager.joinTransaction();
            for (int i = 0; i < carsPartsList.size(); i++) {
                carsPartsList.set(i, this.create(carsPartsList.get(i)));
            }
            this.entityManager.flush();
        } catch (Exception e) {
            this.setEntityException(GenericExceptionType.EX_JPA, -1, "CarsPartsRepositorySameEM", "saveParts()", "Error al persistir las CarsParts", null, e.getMessage());
            //throw e;
        }
        return carsPartsList;
    }
}

Наблюдения: EntityManager внедряется из метода Producer, который динамически создает EntityManagerFactory в зависимости от конфигурации.

Кроме того, поскольку документация EclipseLink относится к настройке ServerPlatform, которая будет использоваться для включения интеграции с контейнером хоста, мне пришлось добавить в файл persistence.xml свойство

<property name="eclipselink.target-server" value="Glassfish"/>

Используя это свойство, убедитесь, что динамически создаваемый EntityManager будет использовать тип транзакции JTA.

Спасибо за ответы и надеюсь, что этот пример может помочь любому, кто сталкивается с подобной проблемой.

0 голосов
/ 14 января 2019

В JPA нет поддержки вложенных транзакций, и нет известного способа сделать это "правильно". Любой частичный откат данных базы данных также потребует частичного отката состояния объекта, что, как правило, единственная разумная вещь, которую необходимо сделать после исключения, это выбросить ваш EntityManager и связанные объекты.

Если вы используете JavaEE и все «создание автомобиля с деталями» заключено в одну транзакцию, это поведение должно работать «из коробки». Если вы используете отдельные транзакции (как я уже говорил: нет способа сделать их вложенными), то возможно, что автомобиль сохраняется, а часть - нет.

Очевидное решение состоит в том, чтобы просто использовать одну транзакцию, управляемую контейнером, для обоих.

...