Spring Data

概念

Spring Data JPA 是 Spring Data 系列的一部分,可以轻松实现基于 JPA 的存储库。 该模块处理对基于 JPA 的数据访问层的增强的支持。 这使得使用数据访问技术构建Spring 的应用程序变得更加容易。

传统方式访问数据库JDBC驱动

创建Maven项目

  • maven工程的目录结构
    |– pom.xml
    |– src
    | |– main
    | | -- java | |– resources
    | | -- filters |– test
    | | -- java | |– resources
    | | -- filters |– it
    | -- assembly |– site
    |– LICENSE.txt
    |– NOTICE.txt
    |– README.txt
  • 添加依赖
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <!--MySQL Driver-->
    <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.38</version>
    </dependency>
    <!--junit-->
    <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.10</version>
    <scope>test</scope>
    </dependency>

数据表准备

  • Create database spring_data;
  • create table student(
    id int not null auto_increment,
    name varchar(20) not null,
    age int not null,
    primary key(id)
    );
  • Insert into student (name, age) values (“zhangsan”,20);
  • Insert into student (name, age) values (“lisi”,21);
  • Insert into student (name, age) values (“wangwu”,22);

    开发JDBCUtil工具类

  • 获取Connection,关闭Connection、Statement、ResultSet
  • 注意事项
    配置的属性放置在配置文件中,通过代码的方式将配置文件中的属性加载进程序

    建立对象模型,DAO

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    package com.imooc.domain;
    /**
    * Student实体类
    */
    public class Student {
    /**
    * 主键字段
    */
    private int id;
    /**
    * 姓名
    */
    private String name;
    /**
    * 年龄
    */
    private int age;
    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;
    }
    public int getAge() {
    return age;
    }
    public void setAge(int age) {
    this.age = age;
    }
    @Override
    public String toString() {
    return "Student{" +
    "id=" + id +
    ", name='" + name + '\'' +
    ", age=" + age +
    '}';
    }
    }

使用Spring JDBC的方式操作数据库

添加依赖

1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.3.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.5.RELEASE</version>
</dependency>

配置beans.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/spring_data"/>
<property name="username" value="root"/>
<property name="password" value="linux"/>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="studentDAO" class="com.imooc.dao.StudentDAOSprignJDBCImpl">
<property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean>

开发Spring JDBC版本的query和save方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
package com.imooc.dao;
import com.imooc.domain.Student;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowCallbackHandler;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
/**
* StudentDAO访问接口实现类:通过SPring JDBC的方式操作
*/
public class StudentDAOSprignJDBCImpl implements StudentDAO{
private JdbcTemplate jdbcTemplate;
public List<Student> query() {
final List<Student> students = new ArrayList<Student>();
String sql = "select id, name, age from student";
jdbcTemplate.query(sql, new RowCallbackHandler() {
public void processRow(ResultSet rs) throws SQLException {
Student student = new Student();
int id = rs.getInt("id");
String name = rs.getString("name");
int age = rs.getInt("age");
student.setId(id);
student.setAge(age);
student.setName(name);
students.add(student);
}
});
return students;
}
public void save(Student student) {
String sql = "insert into student (name, age) values (?, ?)";
jdbcTemplate.update(sql, student.getName(), student.getAge());
}
public JdbcTemplate getJdbcTemplate() {
return jdbcTemplate;
}
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
}

传统方式访问数据库的缺点

  1. DAO里面代码很多
  2. DAOImpl有很多重复的代码
  3. 开发分页和其他功能需要重新封装

Spring Data JPA快速起步

开发环境搭建

pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<dependencies>
<!--MySQL Driver-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.38</version>
</dependency>
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
</dependency>
<!--spring-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.3.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.5.RELEASE</version>
</dependency>
<!--spring data jpa-->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>1.8.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>4.3.6.Final</version>
</dependency>
</dependencies>

beans.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<!--配置数据源-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/spring_data"/>
<property name="username" value="root"/>
<property name="password" value="linux"/>
</bean>
<!--配置EntityManagerFactory-->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/>
</property>
<property name="packagesToScan" value="com.imooc"/>
<property name="jpaProperties">
<props>
<prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.ImprovedNamingStrategy</prop>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.format_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
</bean>
<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<!--支持注解的事务-->
<tx:annotation-driven transaction-manager="transactionManager"/>
<!--配置Spring data-->
<jpa:repositories base-package="com.imooc" entity-manager-factory-ref="entityManagerFactory"/>
<context:component-scan base-package="com.imooc" />
</beans>

Spring data JPA进阶

Repository接口讲解

  1. Repository是Spring Data的核心接口,不提供任何方法
  2. Public interface Repository{ }
  3. @RepositoryDefinition注解的使用
  4. Repository是一个空接口,也叫做标记接口(没有包含方法声明的接口)
  5. 如果我们定义的EmployeeRepository extends Repository,如果我们自己的接口没有继承Repository接口,会报错
  6. 添加注解能够达到不用extends Repository接口 的功能
    1
    2
    3
    4
    @RepositoryDefinition(domainClass = Employee.class, idClass = Integer.class)
    public interface EmployeeRepository{
    public Employee findByName(String name);
    }

Repository子接口讲解

  1. CrudRepository:继承Repository,实现CRUD相关的方法
  2. PagingAndSortingRepositoy:继承CrudRepository,实现了分页排序相关的方法
  3. JPARepository:继承PagingAndSortingRepository,实现JPA规范相关的方法

Repository中查询方法定义规则和使用

  • Alt text
  • Alt text
  • 接口代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public interface EmployeeRepository extends Repository<Employee, Integer>{
    public Employee findByName(String name);
    // where name like ?% and age < ?
    public List<Employee> findByNameStartingWithAndAgeLessThan(String name, Integer age);
    // where name like %? and age < ?
    public List<Employee> findByNameEndingWithAndAgeLessThan(String name, Integer age);
    // where name in (?,?) or age < ?
    public List<Employee> findByNameInOrAgeLessThan(List<String> names, Integer age);
    // where name in (?,?) and age < ?
    public List<Employee> findByNameInAndAgeLessThan(List<String> names, Integer age);
    }
  • 按照方法命名规则来使用的话的弊端:

    1. 方法名比较常:约定大于配置
    2. 对于一些复杂查询,很难实现

4. Query注解的使用

1. 在Repository方法中使用,不需要遵循查询方法命名规则
1
2
@Query("select o from Employee o where id=(select max (id) from Employee t1)")
public Employee getEmployeeByMaxId();
2. 只需要将@Query定义在Repository中的方法之上即可
    3. 支持命名参数和索引参数的使用
1
2
3
4
5
6
@Query("select o from Employee o where o.name=?1 and o.age=?2")
public List<Employee> queryParams1(String name, Integer age);
@Query("select o from Employee o where o.name=:name and o.age=:age")
public List<Employee> queryParams2(@Param("name") String name,
@Param("age") Integer age);
4. 支持本地查询
1
2
@Query(nativeQuery = true, value = "select COUNT(1) from employee")
public long getCount();

5. 更新和删除操作整合事务的使用

  1. `@Modifying`注解使用
  2. `@Modifying`结合@Query注解执行更新操作
  2. `@Transactional`在Spring Data中的使用
  4. 事务在Spring中的使用:
      * 事务一般是在Service层
* @Query、@Modifying、@Transactional的综合使用

Spring Data JPA高级

CrudRepository接口使用详解

PagingAndSortingRepository接口使用详解

  1. 分页和排序
  2. 带排序的查询

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    @Test
    public void testPage() {
    //page: index是从0开始的
    Pageable pageable = new PageRequest(0,5);
    Page<Employee> page = employeePagingAndSortingRepository.findAll(pageable);
    System.out.println("查询的总页数:" + page.getTotalPages());
    System.out.println("查询的总记录数:" + page.getTotalElements());
    System.out.println("查询当前第几页:" + page.getNumber() + 1);
    System.out.println("查询的当前页面的集合:" + page.getContent());
    System.out.println("查询的当前页面的记录数:" + page.getNumberOfElements());
    }
  3. 带排序的分页查询

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    @Test
    public void testPageAndSort() {
    Sort.Order order = new Sort.Order(Sort.Direction.DESC, "id");
    Sort sort = new Sort(order);
    //page: index是从0开始的
    Pageable pageable = new PageRequest(0,5, sort);
    Page<Employee> page = employeePagingAndSortingRepository.findAll(pageable);
    System.out.println("查询的总页数:" + page.getTotalPages());
    System.out.println("查询的总记录数:" + page.getTotalElements());
    System.out.println("查询当前第几页:" + page.getNumber() + 1);
    System.out.println("查询的当前页面的集合:" + page.getContent());
    System.out.println("查询的当前页面的记录数:" + page.getNumberOfElements());
    }

JpaRepository接口使用详解

  1. findAll
  2. findAll(Sort sort)
  3. Save(entities)
  4. Flush
  5. deleteInBatche(enties)

    JpaSpecificationExcutor接口使用详解

  • 封装了JPA Criteria查询条件
Donate comment here