Ошибка синхронизации / фиксации JPA POJO DTO, даже если я не хочу ее сохранять - PullRequest
0 голосов
/ 15 сентября 2011

Из-за отсутствия ключевых слов для описания этого сценария, позвольте мне перейти к его описанию.Классы были упрощены.

Учитывая это:

public ItemController {
    @Autowired
    ItemDtoService ItemDtoService;

    @Autowired
    DiscountService discountService;
    @RequestMapping(value = "/viewItems", method = RequestMethod.POST)
    public void process() {
        List<ItemDto> ItemDtos = ItemDtoService.getItemDtos();
        for(ItemDto i: ItemDtos) {
            boolean isDiscounted = discountService.hasDiscount(i); //throws exception here on iteration 2 and the last iteration, ItemDto was discounted
            if (isDiscounted) {
                i.setPrice(discountService.getDiscountedPrice(i));
                //do some other i.setter, basically modify the pojo
            }
        }        
    }
}

Исключение выдается в discountService.hasDiscount, когда:

  1. при последующей итерации
  2. и предыдущей итерации ItemDto был сброшен.

Исключение составляет:

Caused by: org.hibernate.exception.SQLGrammarException: could not update: [somepackage.ItemDto#364] 

И где-то в трассировке стека вы увидите это:

at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:456)"

Проблема в том, что вызов метода использует метод daoпод ним @Transactional (и, возможно, по уважительной причине, хотя это всего лишь сложный запрос).Когда диспетчер передачи JPA выполняет свою работу после завершения вызова метода, он видит, что pojo изменено, и пытается синхронизировать его.Pojo ItemDto действительно имеет @Entity, потому что внутри ItemDtoService.getItemDtos использует getEntityManager (). CreateNativeQuery (nativeSql, ItemDto.class).5 других деталей класса здесь:

@Entity
public class ItemDto{
    //body
}


@Service
public class ItemService {
    @Autowired
    ItemDao itemDao;

    public List<ItemDto> getItems() {
        return itemDao.getItems(); //for sake of simplicity     
    }
}

@Repository
@Transactional
public class ItemDaoImpl {
    public List<ItemDto> getItems() {       
        String nativeSql = "select...."
        return getEntityManager().createNativeQuery(nativeSql, ItemDto.class);      
    }

}

@Service
public class DiscountService {
    @Autowired
    DiscountDao discountDao;

    public boolean hasDiscount(ItemDto i) {     
        boolean hasDiscount = discountDao.hasDiscount(i);
        //do other service stuff that might influence the hasDiscount flag
        return hasDiscount;     
    }
}

@Repository
@Transactional
public class DiscountDaoImpl {
    public boolean hasDiscount(ItemDto i) {     
        String nativeSql = "select...."
        boolean hasDiscount;
        //in reality the query is a complicated joins, executes and returns if has discount or not
        return hasDiscount;
    }

}

Что я делаю не так?

Некоторые из опций, которые я пробовал и работал, включают:

  1. добавление к @Transactional (readonly = true) методов Dao, так как они являются только запросами (хотя отрицательный эффектони могут быть преднамеренно транзакционными из-за сложных запросов и могут нуждаться в блокировке для предотвращения грязного чтения)
  2. в контроллере, создать отдельный цикл для модификации, затем он имеет 2 цикла, 1 для циклического просмотра элементов и просмотрасо скидкой, храните эту информацию где-нибудь, чтобы потом на нее ссылаться во втором цикле, который выполняет модификацию указанных pojos

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

1 Ответ

0 голосов
/ 15 сентября 2011

Другой вариант, который я только что нашел, находится внутри Dao, который возвращает список ItemDto, перед тем, как вернуть список, я бы выполнил это:

getEntityManager().clear();

Он работает нормально, потому что в любом случае список DtoОжидайте, что они не требуют синхронизации БД, в то же время @Transactional сохраняется для необходимой блокировки для согласованного чтения.

Это еще одна альтернатива, но какой самый подходящий способ на самом деле?

...