Java持久化API,即Java Persistence API (JPA),使用对象-关系映射持久化数据。
一: 配置EntityManager
EntityManage中是一个接口,定义了增、删、改、查的一些基本操作,都是由EntityManagerFactory创建,根据EntityManagerFactory创建和管理方式不同,可分为两类:
(1)应用程序管理类型:PersistenceProvider中方法createEntityManagerFactory创建EntityManagerFactory
(2)容器管理类型:PersistenceProvider中方法createContainerEntityManagerFactory创建EntityManagerFactory
Spring在查找entityManagerFactory实例时(?这个地方没有看懂),会查找到AbstractEntityManagerFactoryBean类,afterPropertiesSet方法中创建EntityManagerFactory实例及其代理,创建EntityManagerFactory的方法createNativeEntityManagerFactory为抽象方法,该方法在AbstractEntityManagerFactoryBean类的两个子类LocalContainerEntityManagerFactoryBean、LocalEntityManagerFactoryBean中实现,因此配置LocalContainerEntityManagerFactoryBean和LocalEntityManagerFactoryBean的bean实现,即可配置EntityManagerFactory。
1、配置应用程序管理类型的JPA(未实现)
2、配置容器管理类型的JPA
配置LocalContainerEntityManagerFactoryBean(本例中使用mysql数据库)
package test.config;
import org.apache.commons.dbcp2.BasicDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.Database;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import javax.sql.DataSource;
@Configurationpublic class JpaConfiguration {
@Bean
public JpaTransactionManager transactionManager() {
return new JpaTransactionManager();
}
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource,
JpaVendorAdapter jpaVendorAdapter) {
LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
emf.setDataSource(dataSource);
emf.setJpaVendorAdapter(jpaVendorAdapter);
emf.setPackagesToScan("test");
return emf;
}
}
DataSource及JpaVendorAdapter的参数设置需要与使用的数据库对应。
其中DataSource为commons-dbcp2-2.5.0.jar包中的DataSource实现:BasicDataSource,配置用户名、密码、url。driver等
@Bean
public DataSource dateSource() throws Exception {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriver(new com.mysql.cj.jdbc.Driver());
dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/test?userUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8");
dataSource.setUsername("root");
dataSource.setPassword("admin");
dataSource.setInitialSize(5);
dataSource.setMaxTotal(10);
return dataSource;
}
JpaVendorAdapter有多种实现,这里选择HibernateJpaVendorAdapter
@Bean
public HibernateJpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
adapter.setDatabase(Database.MYSQL);
adapter.setShowSql(true);
adapter.setGenerateDdl(true);
adapter.setDatabasePlatform("org.hibernate.dialect.MySQLDialect");
return adapter;
}
二: 编写基于JPA的Repository
可以注入EntityManagerFactory或者EntityManager实例,操作数据库。
假设数据库为category_,对应的类为Category,注解对应的字段
package test;
import javax.persistence.*;
@Entity
@Table(name = "category_")
public class Category {
@Id
@GeneratedValue(strategy= GenerationType.IDENTITY)
private int id;
@Column(name="name")
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Category{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
junit测试如下:
package test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import test.config.JpaConfiguration;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceUnit;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {JpaConfiguration.class})
public class EntityManagerFactoryTest {
@PersistenceUnit
private EntityManagerFactory emf;
@PersistenceContext
private EntityManager em;
@Test
public void test() {
Category category = emf.createEntityManager().find(Category.class, 52);
System.out.println(category);
category = em.find(Category.class, 52);
System.out.println(category);
}
}
通过@PersistenceUnit注解注入EntityMangerFactory时,每次操作数据库都会创建一个EntityManager,而EntityManager并不是线程安全的,会造成线程安全性问题;
通过@PersistenceContext注解注入EntityManager时,实际上注入了一个EntityManager的代理,如果当前事务没有对应的EntityManager代理,则会创建一个新的。
三: 借助Spring Data实现自动化的JPA Repository
以接口的方式,创建Repository
package test;
import org.springframework.data.jpa.repository.JpaRepository;
public interface CategoryRepository extends JpaRepository<Category, Long> {
}
注入CategoryRepository对象,可以使用Repository接口中定义的保存、删除、根据id查询方法。
配置Spring Data JPA:
在JPAconfiguration类上增加 @EnableJpaRepositories(basePackages="test")注解(basePackages指明扫描的包)
1、定义查询方法
查询方法定义规则如下:
查询动词 + 限制结果条件,其中查询动词可以为get、read、find以及count,get、read、find会返回对象,而count返回匹配对象的数量。
例如 readByName(String name) 会根据name属性查找。
readByNameIgnoringCase(String name) : 忽略名称大小写
具体查询方法定义见<<Spring in action4>> 11.3.1章节
2、声明自定义查询
如果方法命名约定很难表达预期,如查询 select c from category_ c where c.name like 'A%a' 语句对应的结果,
可以在CategoryRepository中使用@Query注解定义如下的查询方法(Category为对应的类名,不是数据库中表名)
@Query("select c from Category c where c.name like 'A%a'")
List<Category> findAllStartsWithA();
注入CategoryRepository方法后即可使用该方法。
3、混合自定义的功能
Spring Data JPA为接口生成实现的时候,会查找与接口名称相同并且添加了Impl后缀的一个类。如果这个类存在,Spring Data JPA将会把它的方法与Spring Data JPA所生成的方法合并在一起。
如果方法命名约定或使用@Query都无法实现所需的方法,对CategoryRepository接口,可以在CategoryRepositoryImpl类中实现自定义的方法,将方法定义为接口
public interface CategoryUpdate {
int updateCategory();
}
CategoryRepositoryImpl中实现该接口
package test;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.test.annotation.Rollback;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
public class CategoryRepositoryImpl implements CategoryUpdate {
@PersistenceContext
private EntityManager em;
public int updateCategory() {
//这里写入需要执行的语句
String sql = "update Category c set c.name = 'Aa' where c.name = 'AA'";
return em.createQuery(sql).executeUpdate();
}
}
修改CategoryRepository,使其继承自CategoryUpdate
package test;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import java.util.List;
public interface CategoryRepository extends JpaRepository<Category, Long>, CategoryUpdate {
Category findByName(String name);
@Query("select c from Category c where c.name like 'A%'")
List<Category> findAllStartsWithA();
}
junit进行测试:
@Autowired
private CategoryRepository categoryRepository;
@Test
@Modifying
@Transactional
@Rollback(false)
public void test3() {
categoryRepository.updateCategory();
}
可以指定@EnableJpaRepositories中repositoryImplementationPostfix属性来设置CategoryRepository接口的实现类名称,其默认为Impl。