Spring in action读书笔记(九) Spring与Java持久化API(JPA)

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。

项目下载

posted @ 2020-01-09 00:12  笪笠  阅读(310)  评论(0编辑  收藏  举报