开发者

JPA synch/commit error of a POJO DTO even if I do not want to save it

开发者 https://www.devze.com 2023-04-05 04:01 出处:网络
Due to lack of key words to capture this scenario, let me just proceed to describe it. The classes have been simplified.

Due to lack of key words to capture this scenario, let me just proceed to describe it. The classes have been simplified.

Given this:

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

An exception is thrown at the discountService.hasDiscount when:

  1. on subsequent iteration
  2. and the previous iteration, the ItemDto wa开发者_JAVA百科s discounted.

Exception is:

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

And somewhere in the stacktrace you will see this:

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

The problem is that method call uses a dao method underneath that is @Transactional (and maybe for a good reason even though it's only a query, complicated query). When the JPA Tx manager does its job upon method call end, it sees the pojo as modified and tries to synch it. The ItemDto pojo does have @Entity because inside ItemDtoService.getItemDtos uses the getEntityManager().createNativeQuery(nativeSql, ItemDto.class). The 5 other class details are here:

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

}

What am I doing wrong?

Some of the options I tried and worked include:

  1. add to the @Transactional the (readonly=true) on the Dao methods since they are only queries (negative effect though is those might be intentionally transactional due to complex queries, and may need locking to prevent dirty reads)
  2. in the Controller, create a separate loop for modification, it then have 2 loops, 1 for looping through items and seeing which is discounted, store those info somewhere to be referenced later on 2nd loop, which does the modification of said pojos

I am looking at other options, and please comment if you see something wrong with the way it was coded.


Another option I just found is inside the Dao that returns the list of ItemDto, before returning the list, I would execute this:

getEntityManager().clear();

It works fine because the list is Dto anyways and one would expect that these require no DB synching, at the same time the @Transactional is retained for necessary locking for consistent reads.

That's one more alternative, but what is the most appropriate way really?

0

精彩评论

暂无评论...
验证码 换一张
取 消