我有一个简单的PoC来测试JPA实施。
@Entity
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Getter private Long id;
private String firstName;
@Getter @Setter private String lastName;
protected Customer() {
}
public Customer(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
}
@Repository
public class ConcreteCustomerRepository {
@PersistenceContext
protected EntityManager entityManager;
void save(Customer customer) {
entityManager.persist(customer);
}
List<Customer> findByLastName(String lastName) {
return entityManager.createQuery("from Customer c where c.lastName = :name")
.setParameter("name", lastName).getResultList();
}
void saveWithIdInTheObjectAndOldLastName(Customer customer, String oldName) {
Query query = entityManager.createQuery(
"update Customer c set c.lastName = :ln where c.id = :id and c.lastName = :oln")
.setParameter("ln", customer.getLastName())
.setParameter("id", customer.getId())
.setParameter("oln", oldName);
if (query.executeUpdate() == 0)
{
throw new NoRowUpdatedException();
}
}
}
具有如下配置:
@Configuration
@EnableTransactionManagement
@PropertySource("classpath:application.properties")
public class TestConfig {
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource, Environment env) {
LocalContainerEntityManagerFactoryBean em
= new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource);
em.setPackagesToScan(new String[] { "com.company.dao.hello.model" });
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
em.setJpaProperties(additionalProperties(env));
return em;
}
@Bean
public DataSource dataSource(@Value("${database.driver}") String dsDriverClassName,
@Value("${database.url}") String dsUrl,
@Value("${database.username}") String dsUsername,
@Value("${database.password}") String dsPassword){
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(dsDriverClassName);
dataSource.setUrl(dsUrl);
dataSource.setUsername(dsUsername);
dataSource.setPassword(dsPassword);
return dataSource;
}
@Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory emf) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}
@Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation(){
return new PersistenceExceptionTranslationPostProcessor();
}
Properties additionalProperties(Environment env) {
Properties properties = new Properties();
properties.setProperty(AvailableSettings.HBM2DDL_AUTO, "create-drop");
properties.setProperty(AvailableSettings.DIALECT, env.getRequiredProperty("database.vendorTypeRaw"));
properties.setProperty(AvailableSettings.SHOW_SQL, "true");
properties.setProperty(AvailableSettings.FORMAT_SQL, "true");
return properties;
}
@Bean
public PropertySourcesPlaceholderConfigurer configurer() {
return new PropertySourcesPlaceholderConfigurer();
}
@Bean
public ConcreteCustomerRepository repository() {
return new ConcreteCustomerRepository();
}
}
以下测试因抛出NoRowUpdatedException而失败:
@ContextConfiguration(classes = {TestConfig.class})
@RunWith(SpringJUnit4ClassRunner.class)
@Transactional
public class SimpleRepositoryTest {
@Autowired
private ConcreteCustomerRepository repository;
@Test
public void shouldBeAbleToUpdateWithAdditionalAttribute() throws InterruptedException {
Customer c = new Customer("M", "Ichsan");
repository.save(c);
Customer retrieved = repository.findByLastName("Ichsan").get(0);
retrieved.setLastName("Jackson");
repository.saveWithIdInTheObjectAndOldLastName(retrieved, "Ichsan"); // Fails here!
retrieved = repository.findByLastName("Jackson").get(0);
assertThat(retrieved.getLastName(), equalTo("Jackson"));
}
}
但是,如果我使存储库使用@PersistentUnit
而不是@PersistenceContext
,它会起作用:
@Repository
public class ConcreteCustomerRepository {
@PersistenceUnit
private EntityManagerFactory emf;
void save(Customer customer) {
EntityManager entityManager = emf.createEntityManager();
entityManager.getTransaction().begin();
entityManager.persist(customer);
entityManager.getTransaction().commit();
}
List<Customer> findByLastName(String lastName) {
EntityManager entityManager = emf.createEntityManager();
return entityManager.createQuery("from Customer c where c.lastName = :name")
.setParameter("name", lastName).getResultList();
}
void saveWithIdInTheObjectAndOldLastName(Customer customer, String oldName) {
EntityManager entityManager = emf.createEntityManager();
entityManager.getTransaction().begin();
Query query = entityManager.createQuery(
"update Customer c set c.lastName = :ln where c.id = :id and c.lastName = :oln")
.setParameter("ln", customer.getLastName())
.setParameter("id", customer.getId())
.setParameter("oln", oldName);
if (query.executeUpdate() == 0)
{
throw new NoRowUpdatedException();
}
entityManager.getTransaction().commit();
}
}
我听说建议使用@PersistentContext
,但遗憾的是它不起作用。这是怎么回事?