我制作了我的第一个Spring + Hibernate + PostgreSQL项目,并遇到了getCurrentSession().save()
的hibernate解释的奇怪行为。
这是我的实体:
@Entity
@Table(name = "orders")
@Cacheable
@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "order_id")
private Long id;
@Version
private long version;
@Column(name = "delivery_date")
private Date deliveryDate;
@Column(name = "delivery_time")
private Time deliveryTime;
@Column(name = "user_id")
private Long userId;
@Column(name = "delivery_address")
private String deliveryAddress;
@Column(name = "executor_id")
private Long executorId;
private String comment;
@OneToMany(fetch = FetchType.EAGER, mappedBy = "order", cascade = CascadeType.ALL, orphanRemoval = true)
@JsonManagedReference
private List<BasketUnit> basketUnitList = new ArrayList<>();
和
@Entity
@Table(name = "basket")
public class BasketUnit {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "unit_id")
private Long id;
@Version
private long version;
@Column(name = "item_id")
private Long itemId;
private int quantity;
@ManyToOne
@JsonBackReference
@JoinColumn(name = "order_id")
private Order order;
HibernateConfig:
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.hibernate5.HibernateTransactionManager;
import org.springframework.orm.hibernate5.LocalSessionFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
import java.util.Properties;
@Configuration
@EnableTransactionManagement
@PropertySource(value = {"classpath:hibernate.properties"})
public class HibernateConfig {
private Environment environment;
@Autowired
HibernateConfig(Environment environment){
this.environment = environment;
}
@Bean
public LocalSessionFactoryBean sessionFactory(){
LocalSessionFactoryBean localSessionFactoryBean = new LocalSessionFactoryBean();
localSessionFactoryBean.setDataSource(dataSource());
localSessionFactoryBean.setPackagesToScan("io.delivery.entity");
localSessionFactoryBean.setHibernateProperties(hibernateProperties());
return localSessionFactoryBean;
}
@Bean
public DataSource dataSource(){
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(environment.getProperty("hibernate.driverClass"));
dataSource.setUrl(environment.getProperty("hibernate.url"));
dataSource.setUsername(environment.getProperty("hibernate.username"));
dataSource.setPassword(environment.getProperty("hibernate.password"));
return dataSource;
}
private Properties hibernateProperties(){
Properties properties = new Properties();
properties.put("hibernate.dialect", environment.getRequiredProperty("hibernate.dialect"));
properties.put("hibernate.show_sql", environment.getRequiredProperty("hibernate.show_sql"));
properties.put("hibernate.format_sql", environment.getRequiredProperty("hibernate.format_sql"));
properties.put("hibernate.hbm2ddl.auto", environment.getRequiredProperty("hibernate.hbm2ddl.auto"));
properties.put("show_sql", environment.getRequiredProperty("hibernate.show_sql"));
properties.put("hibernate.cache.region.factory_class", environment.getRequiredProperty("hibernate.cache.region.factory_class"));
return properties;
}
@Bean
@Autowired
public HibernateTransactionManager transactionManager(SessionFactory sessionFactory){
HibernateTransactionManager transactionManager = new HibernateTransactionManager();
transactionManager.setSessionFactory(sessionFactory);
return transactionManager;
}
}
DAO课程:
@Transactional
public abstract class BasicDaoImpl<T> implements BasicDao<T> {
@Autowired
protected SessionFactory sessionFactory;
private Class<T> entityClass;
public BasicDaoImpl(Class<T> entityClass) {
this.entityClass = entityClass;
}
@Override
public Session getCurrentSession() {
return sessionFactory.getCurrentSession();
}
@Override
public T create(T entity) {
getCurrentSession().save(entity);
return entity;
}
我有服务的模块测试,每个测试调用create(entity)
。
如果我一个接一个地运行测试,但是如果我运行测试类,我会在任何第二次测试时得到HibernateOptimisticLockingFailureException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1; nested exception is org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1
。
在日志中我看到Hibernate在第一次运行时在hql中解释save()
:
Hibernate:
insert
into
orders
(comment, delivery_address, delivery_date, delivery_time, executor_id, user_id, version)
values
(?, ?, ?, ?, ?, ?, ?)
Hibernate:
insert
into
basket
(item_id, order_id, quantity, version)
values
(?, ?, ?, ?)
但是对于第二次和其他运行:
Hibernate:
insert
into
orders
(comment, delivery_address, delivery_date, delivery_time, executor_id, user_id, version)
values
(?, ?, ?, ?, ?, ?, ?)
Hibernate:
update
basket
set
item_id=?,
order_id=?,
quantity=?,
version=?
where
unit_id=?
and version=?
即。 update
代替insert
,最有可能导致错误。
这种行为可能是什么原因以及如何使save()
正常工作?
或者问题可能有些不同?
更新
测试类:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {AppConfig.class, OrderServiceImpl.class, HibernateConfig.class})
public class OrderServiceTest {
@Autowired
private OrderService orderService;
private static final int TEST_QUANTITY = 42;
private static final String TEST_DELIVERY_ADDRESS = "Nowhere";
private static final String TEST_DELIVERY_COMMENT = "Notext";
private static List<BasketUnit> basketUnits = new ArrayList<>();
static {
basketUnits.add(new BasketUnit(20L));
basketUnits.add(new BasketUnit(21L));
basketUnits.add(new BasketUnit(22L));
basketUnits.add(new BasketUnit(23L));
}
@Test
public void updateOrder() {
Order order = createOrder();
assertNotNull(order);
order.setDeliveryAddress(TEST_DELIVERY_ADDRESS);
order.setComment(TEST_DELIVERY_COMMENT);
Order updatedOrder = orderService.updateOrder(order);
assertNotNull(updatedOrder);
assertEquals(TEST_DELIVERY_ADDRESS, updatedOrder.getDeliveryAddress());
assertEquals(TEST_DELIVERY_COMMENT, updatedOrder.getComment());
orderService.deleteOrder(order.getId());
}
@Test
public void updateBasketUnit() {
Order order = createOrder();
assertNotNull(order);
assertNotNull(order.getBasketUnitList().get(0));
order.getBasketUnitList().get(0).setQuantity(TEST_QUANTITY);
Order updatedOrder = orderService.updateOrder(order);
assertNotNull(updatedOrder);
assertNotNull(updatedOrder.getId());
assertEquals(TEST_QUANTITY, updatedOrder.getBasketUnitList().get(0).getQuantity());
orderService.deleteOrder(order.getId());
}
@Test
public void deleteBasketUnit() {
Order order = createOrder();
assertNotNull(order);
List<BasketUnit> basket = order.getBasketUnitList();
assertNotNull(basket);
assertNotNull(basket.get(0));
long id = basket.get(0).getId();
orderService.deleteBasketUnitById(id);
assertNull(orderService.findBasketUnitById(id));
orderService.deleteOrder(order.getId());
}
@Test
public void addBasketUnit() {
Order order = createOrder();
assertNotNull(order);
BasketUnit basketUnitToAdd = new BasketUnit(19L);
order.addBasketUnit(basketUnitToAdd);
Order updatedOrder = orderService.updateOrder(order);
assertThat(updatedOrder.getBasketUnitList(), hasItem(hasProperty("id", is(basketUnitToAdd.getId()))));
orderService.deleteOrder(order.getId());
}
@Test
public void GetByUserId() {
Order order = createOrder();
List<Order> ordersByUserId = orderService.findByUserId(order.getUserId());
assertThat(ordersByUserId, hasItem(hasProperty("id", is(order.getId()))));
assertNotNull(ordersByUserId);
orderService.deleteOrder(order.getId());
}
@Test
public void deleteOrder() {
Order order = createOrder();
assertNotNull(order);
Order responceOrder = orderService.deleteOrder(order.getId());
assertNull(orderService.findById(responceOrder.getId()));
}
@Test
public void GetOrderById() {
Order order = createOrder();
Order responceOrder = orderService.findById(order.getId());
assertEquals(order.getId(), responceOrder.getId());
assertNotNull(responceOrder);
orderService.deleteOrder(order.getId());
}
private Order createOrder() {
Order order = prefillOrder();
orderService.create(order);
assertNotNull(order);
return order;
}
private Order prefillOrder() {
Order order = new Order();
order.setUserId(135L);
order.setComment("true comment");
order.setDeliveryDate(Date.valueOf("1984-01-08"));
order.setDeliveryTime(Time.valueOf("04:05:06"));
order.setDeliveryAddress("Moscow");
order.setExecutorId(350L);
order.setBasketUnitList(basketUnits);
return order;
}
答案 0 :(得分:0)
这种行为是由于测试中的下级表项的static
状态所致,因此Hibernate在上下文中将其保留一次。