时间:2017年04月24日星期一
说明:本文部分内容均来自慕课网。@慕课网:http://www.imooc.com
教学示例源码:https://github.com/zccodere/s...
个人学习源码:https://github.com/zccodere/s...
第一章:课程介绍
1-1 课程介绍
什么是Spring Data
主旨:提供一个熟悉的、一致的,基于Spring框架的数据访问框架。
简化数据库的访问。
历史:2010年提出,作者Rod Johnso,Spring Source项目
网址:http://projects.spring.io/spring-data/#quick-start
Spring Data概览
Spring Data包含多个子项目
Spring Data JPA
Spring Data Mongo DB
Spring Data Redis
Spring Data Solr
课程安排
传统方式访问数据库
Spring Data快速起步
Spring Data JPA进阶
Spring Data JPA高级
第二章:使用传统方式访问数据库
2-1 使用传统方式访问数据库
传统方式访问数据库
JDBC
Spring JdbcTemplate
弊端分析
2-2 准备工作
JDBC
Connection
Statement
ResultSet
Test Case
搭建开发环境
创建maven项目
添加数据库驱动和单元测试依赖
数据库表的准备,使用mysql数据库
创建一个Java项目,POM文件如下:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.zccoder</groupId>
<artifactId>myspringdata</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.3.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.6.RELEASE</version>
</dependency>
<!-- spring-data-jpa -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>1.11.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>5.2.10.Final</version>
</dependency>
<!-- mysql -->
<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>
</dependencies>
</project>
完成后的项目结构图:
2-3 JDBCUtil开发
开发JDBCUtil工具类
获取Connection,关闭Connection、Statement、ResultSet
注意事项:配置的属性放置配置文件中,然后通过代码的方式将配置文件中的数据加载进来即可。
代码示例:
package com.myimooc.springdata.jdbc.util;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
/**
* JDBC工具类:
* 1)获取Connection
* 2)释放资源
* Created by ZC on 2017/4/24.
*/
public class JDBCUtils {
/**
* 获取Connection
* @return 所获得到的JDBC的Connection
*/
public static Connection getConnection() throws Exception{
/**
* 不建议大家把配置硬编码到代码中
* 最佳实践:配置性的建议写到配置文件中
* */
// String url = "jdbc:mysql:///springdata";
// String username = "root";
// String password = "root";
// String dirverClass = "com.mysql.jdbc.Driver";
InputStream inputStream = JDBCUtils.class.getClassLoader().getResourceAsStream("db.properties");
Properties properties = new Properties();
properties.load(inputStream);
String url = properties.getProperty("jdbc.url");
String username = properties.getProperty("jdbc.username");
String password = properties.getProperty("jdbc.password");
String driverClass = properties.getProperty("jdbc.driverClass");
Class.forName(driverClass);
Connection connection = DriverManager.getConnection(url,username,password);
return connection;
}
/**
* 释放DB相关资源
* @param resultSet
* @param statement
* @param connection
*/
public static void release(ResultSet resultSet, Statement statement,Connection connection){
if(resultSet != null ){
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(statement != null ){
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(connection != null ){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
2-4 Dao开发
建立对象模型及DAO层开发。
代码演示:
1、对象模型
package com.myimooc.springdata.jdbc.domain;
/**
* Student实体类
* Created by ZC on 2017/4/24.
*/
public class Student {
/** 主键ID */
private Integer id;
/** 姓名 */
private String name;
/** 年龄 */
private int age;
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer 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;
}
}
2、DAO接口
package com.myimooc.springdata.jdbc.dao;
import com.myimooc.springdata.jdbc.domain.Student;
import java.util.List;
/**
* StudentDAO访问接口
* Created by ZC on 2017/4/24.
*/
public interface StudentDao {
/**
* 获取所有学生
* @return 所有学生
*/
List<Student> listStudent();
/**
* 添加一个学生
* @param student 待添加的学生
*/
void saveStudent(Student student);
}
3、DAO实现
package com.myimooc.springdata.jdbc.dao.impl;
import com.myimooc.springdata.jdbc.dao.StudentDao;
import com.myimooc.springdata.jdbc.domain.Student;
import com.myimooc.springdata.jdbc.util.JDBCUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
/**
* StudentDAO访问接口实现类:通过最原始的JDBC的方式操作
* Created by ZC on 2017/4/24.
*/
public class StudentDaoImpl implements StudentDao {
public List<Student> listStudent() {
List<Student> studentList = new ArrayList<Student>();
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
String sql = "select id,name,age from student";
try {
connection = JDBCUtils.getConnection();
preparedStatement = connection.prepareStatement(sql);
resultSet = preparedStatement.executeQuery();
while(resultSet.next()){
Integer id = resultSet.getInt("id");
String name = resultSet.getString("name");
Integer age = resultSet.getInt("age");
Student student = new Student();
student.setId(id);
student.setName(name);
student.setAge(age);
studentList.add(student);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.release(resultSet,preparedStatement,connection);
}
return studentList;
}
public void saveStudent(Student student) {
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
String sql = "insert into student(name,age) values(?,?)";
try {
connection = JDBCUtils.getConnection();
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1,student.getName());
preparedStatement.setInt(2,student.getAge());
preparedStatement.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.release(resultSet,preparedStatement,connection);
}
}
}
4、单元测试
package com.myimooc.springdata.jdbc.dao;
import com.myimooc.springdata.jdbc.dao.impl.StudentDaoImpl;
import com.myimooc.springdata.jdbc.domain.Student;
import org.junit.Test;
import java.util.List;
/**
* StudentDao 单元测试类
* Created by ZC on 2017/4/24.
*/
public class StudentDaoImplTest {
@Test
public void listStudentTest(){
StudentDao studentDao = new StudentDaoImpl();
List<Student> studentList = studentDao.listStudent();
for(Student student : studentList){
System.out.println(student.toString());
}
}
@Test
public void saveStudentTest(){
StudentDao studentDao = new StudentDaoImpl();
Student student = new Student();
student.setName("test");
student.setAge(30);
studentDao.saveStudent(student);
}
}
2-5 使用JdbcTemplate
Spring JdbcTemplate
添加maven依赖
DataSource & JdbcTemplate注入
Test Case
代码演示:
1、创建DB配置文件
jdbc.url = jdbc:mysql:///springdata
jdbc.username = root
jdbc.password = root
jdbc.driverClass = com.mysql.jdbc.Driver
2、创建配置文件类
package com.myimooc.springdata.jdbctemplate.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.ImportResource;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
/**
* 配置参数类
* Created by ZC on 2017/4/24.
*/
@PropertySource(value="classpath:db.properties")
@Component
public class Properties {
@Value("${jdbc.driverClass}")
private String jdbcDriverClass;
@Value("${jdbc.url}")
private String jdbcUrl;
@Value("${jdbc.username}")
private String jdbcUser;
@Value("${jdbc.password}")
private String jdbcPassword;
@Override
public String toString() {
return "Properties{" +
"jdbcDriverClass='" + jdbcDriverClass + '\'' +
", jdbcUrl='" + jdbcUrl + '\'' +
", jdbcUser='" + jdbcUser + '\'' +
", jdbcPassword='" + jdbcPassword + '\'' +
'}';
}
public String getJdbcDriverClass() {
return jdbcDriverClass;
}
public String getJdbcUrl() {
return jdbcUrl;
}
public String getJdbcUser() {
return jdbcUser;
}
public String getJdbcPassword() {
return jdbcPassword;
}
}
3、配置DataSource、JdbcTemplate和Spring注解扫描
package com.myimooc.springdata.jdbctemplate.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
/**
* Created by ZC on 2017/4/24.
*/
@Configuration
@ComponentScan("com.myimooc.springdata.jdbctemplate")
public class SpringConfig {
@Autowired
private Properties properties;
@Bean
DriverManagerDataSource getDriverManagerDataSource(){
DriverManagerDataSource driverManagerDataSource = new DriverManagerDataSource();
driverManagerDataSource.setDriverClassName(properties.getJdbcDriverClass());
driverManagerDataSource.setUrl(properties.getJdbcUrl());
driverManagerDataSource.setUsername(properties.getJdbcUser());
driverManagerDataSource.setPassword(properties.getJdbcPassword());
return driverManagerDataSource;
}
@Bean
JdbcTemplate getJdbcTemplate(){
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(getDriverManagerDataSource());
return jdbcTemplate;
}
}
4、编写实体类
package com.myimooc.springdata.jdbctemplate.domain;
/**
* Student实体类
* Created by ZC on 2017/4/24.
*/
public class Student {
/** 主键ID */
private Integer id;
/** 姓名 */
private String name;
/** 年龄 */
private int age;
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer 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;
}
}
5、DAO接口
package com.myimooc.springdata.jdbctemplate.dao;
import com.myimooc.springdata.jdbctemplate.domain.Student;
import java.util.List;
/**
* StudentDAO访问接口
* Created by ZC on 2017/4/24.
*/
public interface StudentDao {
/**
* 获取所有学生
* @return 所有学生
*/
List<Student> listStudent();
/**
* 添加一个学生
* @param student 待添加的学生
*/
void saveStudent(Student student);
}
6、DAO实现
package com.myimooc.springdata.jdbctemplate.dao.impl;
import com.myimooc.springdata.jdbctemplate.dao.StudentDao;
import com.myimooc.springdata.jdbctemplate.domain.Student;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* StudentDAO访问接口实现类:通过 JdbcTemplate 的方式操作
* Created by ZC on 2017/4/24.
*/
@Repository
public class StudentDaoImpl implements StudentDao {
@Autowired
private JdbcTemplate jdbcTemplate;
public List<Student> listStudent() {
List<Student> studentList = new ArrayList<Student>();
String sql = "select id, name, age from student";
List<Map<String,Object>> mapList = jdbcTemplate.queryForList(sql);
for (Map<String,Object> mapTemp : mapList) {
Integer id = Integer.parseInt(mapTemp.get("id").toString());
String name = mapTemp.get("name").toString();
Integer age = Integer.parseInt(mapTemp.get("age").toString());
Student student = new Student();
student.setId(id);
student.setName(name);
student.setAge(age);
studentList.add(student);
}
return studentList;
}
public void saveStudent(Student student) {
String sql = "insert into student(name, age) value(?,?)";
jdbcTemplate.update(sql,student.getName(),student.getAge());
}
}
7、单元测试类
package com.myimooc.springdata.jdbctemplate;
import com.myimooc.springdata.jdbctemplate.config.SpringConfig;
import com.myimooc.springdata.jdbctemplate.dao.StudentDao;
import com.myimooc.springdata.jdbctemplate.domain.Student;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import javax.sql.DataSource;
import java.util.List;
/**
* 使用 JdbcTemplate 实现 StudentDao 单元测试类
* Created by ZC on 2017/4/24.
*/
public class StudentDaoTest {
private ApplicationContext ctx = null;
private StudentDao studentDao;
@Before
public void init(){
ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
studentDao = ctx.getBean(StudentDao.class);
}
@After
public void destroy(){
ctx = null;
}
@Test
public void listStudentTest(){
List<Student> studentList = studentDao.listStudent();
for (Student student : studentList){
System.out.println(student.toString());
}
}
@Test
public void saveTest(){
Student student = new Student();
student.setName("test-spring-jdbcTemplate");
student.setAge(25);
studentDao.saveStudent(student);
}
}
2-6 弊端分析
弊端分析
DAO里面代码量太多
DAO的实现有很多重复代码
开发分页和其它功能,需要重新进行封装
第三章:Spring Data快速入门
3-1 开发环境搭建
Spring Data JPA快速起步
开发环境搭建
Spring Data JPA HelloWorld开发
代码演示:
1、创建DB配置文件
jdbc.url = jdbc:mysql:///springdata
jdbc.username = root
jdbc.password = root
jdbc.driverClass = com.mysql.jdbc.Driver
2、创建配置文件类
package com.myimooc.springdata.jpa.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
/**
* Created by ZC on 2017/4/24.
*/
@PropertySource(value="classpath:db.properties")
@Component
public class PropertiesConfig {
@Value("${jdbc.driverClass}")
private String jdbcDriverClass;
@Value("${jdbc.url}")
private String jdbcUrl;
@Value("${jdbc.username}")
private String jdbcUser;
@Value("${jdbc.password}")
private String jdbcPassword;
@Override
public String toString() {
return "Properties{" +
"jdbcDriverClass='" + jdbcDriverClass + '\'' +
", jdbcUrl='" + jdbcUrl + '\'' +
", jdbcUser='" + jdbcUser + '\'' +
", jdbcPassword='" + jdbcPassword + '\'' +
'}';
}
public String getJdbcDriverClass() {
return jdbcDriverClass;
}
public String getJdbcUrl() {
return jdbcUrl;
}
public String getJdbcUser() {
return jdbcUser;
}
public String getJdbcPassword() {
return jdbcPassword;
}
}
3、配置TransactionManager、EntityManagerFactory和Spring自动扫描注入
package com.myimooc.springdata.jpa.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.Database;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import java.util.Properties;
/**
* Spring配置类
* Created by ZC on 2017/4/24.
*/
// 声明为配置类
@Configuration
// 启用事务管理
@EnableTransactionManagement
// 启用自动扫描继承 JpaRepository 接口的类。
// 注意,此注解需要配置 entityManagerFactory 和 transactionManager
// 方式一:定义获取Bean方法名为 entityManagerFactory 和 transactionManager
// 方式二:配置 @EnableJpaRepositories注解的 entityManagerFactoryRef 属性 为自定义获取Bean的方法名。
@EnableJpaRepositories(basePackages = "com.myimooc.springdata.jpa")
// 启用自动扫描 @Component 注解的Bean
@ComponentScan(basePackages = "com.myimooc.springdata.jpa")
public class SpringConfig{
@Autowired
private PropertiesConfig propertiesConfig;
/**
* 配置数据源
* @return
*/
@Bean
public DriverManagerDataSource dataSource(){
DriverManagerDataSource driverManagerDataSource = new DriverManagerDataSource();
driverManagerDataSource.setDriverClassName(propertiesConfig.getJdbcDriverClass());
driverManagerDataSource.setUrl(propertiesConfig.getJdbcUrl());
driverManagerDataSource.setUsername(propertiesConfig.getJdbcUser());
driverManagerDataSource.setPassword(propertiesConfig.getJdbcPassword());
return driverManagerDataSource;
}
/**
* 配置事务管理器 JpaTransactionManager
* @return
*/
@Bean(name="transactionManager")
public PlatformTransactionManager transactionManager(){
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setDataSource(this.dataSource());
transactionManager.setEntityManagerFactory(this.entityManagerFactory().getObject());
return transactionManager;
// return new DataSourceTransactionManager(this.dataSource());
// return new JpaTransactionManager(this.entityManagerFactory().getObject());
}
/**
* 配置JPA的 EntityManagerFactory
* @return
*/
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(){
LocalContainerEntityManagerFactoryBean entityManagerFactory = new LocalContainerEntityManagerFactoryBean();
entityManagerFactory.setDataSource(dataSource());
HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
jpaVendorAdapter.setGenerateDdl(true);
jpaVendorAdapter.setDatabase(Database.MYSQL);
entityManagerFactory.setJpaVendorAdapter(jpaVendorAdapter);
entityManagerFactory.setPackagesToScan("com.myimooc.springdata.jpa");
Properties jpaProperties = new Properties();
// jpaProperties.setProperty("hibernate.ejb.naming_strategy","org.hibernate.cfg.ImprovedNamingStrategy");
jpaProperties.setProperty("hibernate.dialect","org.hibernate.dialect.MySQL5InnoDBDialect");
jpaProperties.setProperty("hibernate.show_sql","true");
jpaProperties.setProperty("hibernate.format_sql","true");
jpaProperties.setProperty("hibernate.hbm2ddl.auto","update");
entityManagerFactory.setJpaProperties(jpaProperties);
return entityManagerFactory;
}
}
4、编写实体类:Employee
package com.myimooc.springdata.jpa.domain;
import javax.persistence.*;
/**
* 雇员:先开发实体类,然后自动生成实体表
* Created by ZC on 2017/4/24.
*/
@Entity
@Table(name = "test_employee")
public class Employee {
@Id
@GeneratedValue
private Integer id;
@Column(length = 20)
private String name;
private Integer age;
@Override
public String toString() {
return "Employee{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
3-2 起步程序开发
代码演示:
1、编写EmployeeRepository接口
package com.myimooc.springdata.jpa.repository;
import com.myimooc.springdata.jpa.domain.Employee;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
* 使用 Repository 接口
* Created by ZC on 2017/4/25.
*/
// 方式二:使用 @RepositoryDefinition 注解
// @RepositoryDefinition(domainClass = Employee.class,idClass = Integer.class)
public interface EmployeeRepository extends Repository<Employee,Integer> {//方式一:继承 Repository 接口
/**
* 获取雇员对象通过名称
* @param name
* @return
*/
Employee findByName(String name);
}
2、编写单元测试类:EmployeeRepositoryTest
package com.myimooc.springdata.jpa.repository;
import com.myimooc.springdata.jpa.config.SpringConfig;
import com.myimooc.springdata.jpa.domain.Employee;
import com.myimooc.springdata.jpa.repository.EmployeeRepository;
import com.myimooc.springdata.jpa.service.EmployeeService;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import java.util.ArrayList;
import java.util.List;
/**
* EmployeeRepository单元测试类
* Created by ZC on 2017/4/24.
*/
public class EmployeeRepositoryTest {
private ApplicationContext ctx = null;
private EmployeeRepository employeeRepository = null;
@Before
public void init(){
ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
employeeRepository = ctx.getBean(EmployeeRepository.class);
}
@After
public void destroy(){
ctx = null;
}
@Test
public void entityManageFactoryTest(){
LocalContainerEntityManagerFactoryBean entityManagerFactory = (LocalContainerEntityManagerFactoryBean)ctx.getBean(LocalContainerEntityManagerFactoryBean.class);
Assert.assertNotNull(entityManagerFactory);
}
@Test
public void findByNameTest(){
System.out.println(employeeRepository);
Employee employee = employeeRepository.findByName("cc");
if( null == employee){
System.out.println("查询数据为空");
}else{
System.out.println(employee.toString());
}
}
}
Repository
Repository:Spring Data核心类
RepositoryDefinition:使用该注解进行配置
Repository Query Specification:查询时,方法命名不能乱写
Query Annotation:使用该注解,可以实现原生SQL查询
Update/Delete/Transaction:更新、删除操作,支持事务
Repository Hierarchy
CrudRepository:内置了新增、更新、删除、查询方法
PagingAndSortingRespository:分页和排序
JpaRepository
JpaSpecificationExcutor
第四章:Spring Data JPA进阶
4-1 关于Repository接口
Repository接口详解
Repository接口是Spring Data的核心接口,不提供任何方法
public interface Repository<T, ID extends Serializable>{}
@RepositoryDefinition注解的使用
Repository类的定义:
1)Repository是一个空接口,标记接口。没有包含方法声明的接口
2)如果我们定义的接口EmployeeRepository extends Repository,会被Spring管理。
如果我们自己的接口没有extends Repository,运行时会报错,没有这个Bean。
4-2 Repository子接口详解
Repository子接口详解
CrudRepository:继承Repository,实现了CRUD相关的方法
PagingAndSortingRepository:继承CrudRepository,实现了分页排序相关的方法
JpaRepository:继承PagingAndSortingRepositor,实现JPA规范相关的方法
4-3 查询方法定义规则和使用
Repository中查询方法定义规则和使用
了解Spring Data中查询方法名称的定义规则
使用Spring Data完成复杂查询方法名称的命名
查询方法定义规则
代码演示:
1、在EmployeeRepository接口编写以下代码
// 使用JPA规范查询
// where name like ?% and age < ?
List<Employee> findByNameStartingWithAndAgeLessThan(String name,Integer age);
// where name like %? and age < ?
List<Employee> findByNameEndingWithAndAgeLessThan(String name,Integer age);
// where name in (?,?...) or age < ?
List<Employee> findByNameInOrAgeLessThan(List<String> name,Integer age);
// where name in (?,?...) and age < ?
List<Employee> findByNameInAndAgeLessThan(List<String> name,Integer age);
2、在EmployeeRepositoryTest单元测试类进行测试
@Test
public void findByNameStartingWithAndAgeLessThanTest(){
System.out.println(employeeRepository);
List<Employee> employees = employeeRepository.findByNameStartingWithAndAgeLessThan("test",22);
if( null != employees){
for (Employee employee : employees) {
System.out.println(employee.toString());
}
}else{
System.out.println("查询数据为空");
}
}
@Test
public void findByNameEndingWithAndAgeLessThanTest(){
System.out.println(employeeRepository);
List<Employee> employees = employeeRepository.findByNameEndingWithAndAgeLessThan("6",23);
if( null != employees && employees.size() > 0){
for (Employee employee : employees) {
System.out.println(employee.toString());
}
}else{
System.out.println("查询数据为空");
}
}
@Test
public void findByNameInOrAgeLessThanTest(){
List<String> names = new ArrayList<String>();
names.add("test1");
names.add("test2");
names.add("test3");
List<Employee> employees = employeeRepository.findByNameInOrAgeLessThan(names,22);
if( null != employees && employees.size() > 0){
for (Employee employee : employees) {
System.out.println(employee.toString());
}
}else{
System.out.println("查询数据为空");
}
}
@Test
public void findByNameInAndAgeLessThanTest(){
List<String> names = new ArrayList<String>();
names.add("test1");
names.add("test2");
names.add("test3");
List<Employee> employees = employeeRepository.findByNameInAndAgeLessThan(names,22);
if( null != employees && employees.size() > 0){
for (Employee employee : employees) {
System.out.println(employee.toString());
}
}else{
System.out.println("查询数据为空");
}
}
对于按照方法命名规则来使用的话,有弊端:
1)方法名比较长:约定大于配置
2)对于一些复杂的查询,是很难实现。
使用@Query注解来解决。
4-4 Query注解使用
Query注解使用
在Respository方法中使用,不需要遵循查询方法命令规则
只需要将@Query定义在Respository中的方法之上即可
命名参数及索引参数的使用
本地查询
代码演示:
1、在EmployeeRepository接口编写以下代码
// 使用@Query注解查询
/**
* 自定义查询SQL
* */
@Query("select o from Employee o where id=(select max(id) from Employee t1)")
Employee getEmployeeByMaxId();
/**
* 使用占位符进行参数绑定
* */
@Query("select o from Employee o where o.name=?1 and o.age=?2")
List<Employee> listEmployeeByNameAndAge(String name, Integer age);
/**
* 使用命名参数进行参数绑定
* */
@Query("select o from Employee o where o.name=:name and o.age=:age")
List<Employee> listEmployeeByNameAndAge2(@Param("name") String name, @Param("age")Integer age);
/**
* 自定义查询SQL,like,占位符进行参数绑定
* */
@Query("select o from Employee o where o.name like %?1%")
List<Employee> listEmployeeByLikeName(String name);
/**
* 自定义查询SQL,like,命名参数进行参数绑定
* */
@Query("select o from Employee o where o.name like %:name%")
List<Employee> listEmployeeByLikeName2(@Param("name") String name);
/**
* 使用原生态SQL查询
* @return
*/
@Query(nativeQuery = true,value = "select count(1) from employee")
long getCount();
2、在EmployeeRepositoryTest单元测试类进行测试
// 使用 @Query 注解查询
@Test
public void getEmployeeByMaxIdTest(){
Employee employee = employeeRepository.getEmployeeByMaxId();
if( null != employee ){
System.out.println(employee.toString());
}else{
System.out.println("查询数据为空");
}
}
@Test
public void listEmployeeByNameAndAgeTest(){
List<Employee> employees = employeeRepository.listEmployeeByNameAndAge("zhangsan",20);
if( null != employees && employees.size() > 0){
for (Employee employee : employees) {
System.out.println(employee.toString());
}
}else{
System.out.println("查询数据为空");
}
}
@Test
public void listEmployeeByNameAndAge2Test(){
List<Employee> employees = employeeRepository.listEmployeeByNameAndAge2("zhangsan",20);
if( null != employees && employees.size() > 0){
for (Employee employee : employees) {
System.out.println(employee.toString());
}
}else{
System.out.println("查询数据为空");
}
}
@Test
public void listEmployeeByLikeNameTest(){
List<Employee> employees = employeeRepository.listEmployeeByLikeName("test1");
if( null != employees && employees.size() > 0){
for (Employee employee : employees) {
System.out.println(employee.toString());
}
}else{
System.out.println("查询数据为空");
}
}
@Test
public void listEmployeeByLikeName2Test(){
List<Employee> employees = employeeRepository.listEmployeeByLikeName2("test");
if( null != employees && employees.size() > 0){
for (Employee employee : employees) {
System.out.println(employee.toString());
}
}else{
System.out.println("查询数据为空");
}
}
@Test
public void getCountTest(){
long count = employeeRepository.getCount();
System.out.println(count);
}
4-5 更新操作整合事务使用
更新及删除操作整合事务的使用
@Modifying注解使用
@Modifying结合@Query注解执行更新操作
@Transaction在Spring Data中的使用
事务在Spring data中的使用:
1)事务一般是在Service层
2)@Query、@Modifying、@Transaction的综合使用
代码演示:
1、基于javaconfig在SpringConfig类进行事务配置
package com.myimooc.springdata.jpa.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.Database;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import java.util.Properties;
/**
* Spring配置类
* Created by ZC on 2017/4/24.
*/
// 声明为配置类
@Configuration
// 启用事务管理
@EnableTransactionManagement
// 启用自动扫描继承 JpaRepository 接口的类。
// 注意,此注解需要配置 entityManagerFactory 和 transactionManager
// 方式一:定义获取Bean方法名为 entityManagerFactory 和 transactionManager
// 方式二:配置 @EnableJpaRepositories注解的 entityManagerFactoryRef 属性 为自定义获取Bean的方法名。
@EnableJpaRepositories(basePackages = "com.myimooc.springdata.jpa")
// 启用自动扫描 @Component 注解的Bean
@ComponentScan(basePackages = "com.myimooc.springdata.jpa")
public class SpringConfig{
@Autowired
private PropertiesConfig propertiesConfig;
/**
* 配置数据源
* @return
*/
@Bean
public DriverManagerDataSource dataSource(){
DriverManagerDataSource driverManagerDataSource = new DriverManagerDataSource();
driverManagerDataSource.setDriverClassName(propertiesConfig.getJdbcDriverClass());
driverManagerDataSource.setUrl(propertiesConfig.getJdbcUrl());
driverManagerDataSource.setUsername(propertiesConfig.getJdbcUser());
driverManagerDataSource.setPassword(propertiesConfig.getJdbcPassword());
return driverManagerDataSource;
}
/**
* 配置事务管理器 JpaTransactionManager
* @return
*/
@Bean(name="transactionManager")
public PlatformTransactionManager transactionManager(){
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setDataSource(this.dataSource());
transactionManager.setEntityManagerFactory(this.entityManagerFactory().getObject());
return transactionManager;
// return new DataSourceTransactionManager(this.dataSource());
// return new JpaTransactionManager(this.entityManagerFactory().getObject());
}
/**
* 配置JPA的 EntityManagerFactory
* @return
*/
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(){
LocalContainerEntityManagerFactoryBean entityManagerFactory = new LocalContainerEntityManagerFactoryBean();
entityManagerFactory.setDataSource(dataSource());
HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
jpaVendorAdapter.setGenerateDdl(true);
jpaVendorAdapter.setDatabase(Database.MYSQL);
entityManagerFactory.setJpaVendorAdapter(jpaVendorAdapter);
entityManagerFactory.setPackagesToScan("com.myimooc.springdata.jpa");
Properties jpaProperties = new Properties();
// jpaProperties.setProperty("hibernate.ejb.naming_strategy","org.hibernate.cfg.ImprovedNamingStrategy");
jpaProperties.setProperty("hibernate.dialect","org.hibernate.dialect.MySQL5InnoDBDialect");
jpaProperties.setProperty("hibernate.show_sql","true");
jpaProperties.setProperty("hibernate.format_sql","true");
jpaProperties.setProperty("hibernate.hbm2ddl.auto","update");
entityManagerFactory.setJpaProperties(jpaProperties);
return entityManagerFactory;
}
}
2、在EmployeeRepository接口编写以下代码
// 更新数据
@Transactional
@Modifying
@Query("update Employee o set o.age = :age where o.id = :id")
void updateAgeById(@Param("id")Integer id,@Param("age")Integer age);
3、定义Service层,实际开发中,需要定义接口,这里为了演示方便,直接使用类。
package com.myimooc.springdata.jpa.service;
import com.myimooc.springdata.jpa.domain.Employee;
import com.myimooc.springdata.jpa.repository.EmployeeCrudRepository;
import com.myimooc.springdata.jpa.repository.EmployeeRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
* Created by ZC on 2017/4/25.
*/
@Service
public class EmployeeService {
@Autowired
private EmployeeRepository employeeRepository;
@Autowired
private EmployeeCrudRepository employeeCrudRepository;
@Transactional
public void updateAgeById(Integer id, Integer age){
this.employeeRepository.updateAgeById(id,age);
};
@Transactional
public void save(List<Employee> employees){
this.employeeCrudRepository.save(employees);
}
}
4、编写EmployeeService单元测试类
package com.myimooc.springdata.jpa.service;
import com.myimooc.springdata.jpa.config.SpringConfig;
import com.myimooc.springdata.jpa.repository.EmployeeRepository;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
/**
* EmployeeService单元测试类
* Created by ZC on 2017/4/25.
*/
public class EmployeeServiceTest {
private ApplicationContext ctx = null;
private EmployeeService employeeService = null;
@Before
public void init(){
ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
employeeService = ctx.getBean(EmployeeService.class);
}
@After
public void destroy(){
ctx = null;
}
@Test
public void transactionManagerTest(){
PlatformTransactionManager transactionManager = (PlatformTransactionManager)ctx.getBean(PlatformTransactionManager.class);
Assert.assertNotNull(transactionManager);
}
// 更新操作
@Test
public void updateAgeByIdTest(){
employeeService.updateAgeById(1,55);
}
}
第五章:Spring Data JPA高级
5-1 CrudRepository接口使用详解
CrudRepository接口使用详解
save(entity):保存一个实体
save(entities):保存多个实体
findOne(id):找到一个对象
exists(id):根据ID判断对象是否存在
findAll():找到所有实体对象
delete(id):根据ID删除实体对象
delete(entity):根据实体对象删除实体对象
delete(entities):删除多个实体对象
deleteAll():删除所有实体对象
代码演示:
1、编写EmployeeCrudRepository接口
package com.myimooc.springdata.jpa.repository;
import com.myimooc.springdata.jpa.domain.Employee;
import org.springframework.data.repository.CrudRepository;
/**
* 使用 CrudRepository 接口
* Created by ZC on 2017/4/26.
*/
public interface EmployeeCrudRepository extends CrudRepository<Employee,Integer>{
}
2、编写EmployeeCrudRepositoryTest单元测试类
package com.myimooc.springdata.jpa.repository;
import com.myimooc.springdata.jpa.config.SpringConfig;
import com.myimooc.springdata.jpa.domain.Employee;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import java.util.ArrayList;
import java.util.List;
/**
* EmployeeRepository单元测试类
* Created by ZC on 2017/4/24.
*/
public class EmployeeCrudRepositoryTest {
private ApplicationContext ctx = null;
private EmployeeCrudRepository employeeCrudRepository = null;
@Before
public void init(){
ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
employeeCrudRepository = ctx.getBean(EmployeeCrudRepository.class);
}
@After
public void destroy(){
ctx = null;
}
@Test
public void saveTest(){
List<Employee> employees = new ArrayList<Employee>();
Employee employee = null;
for(int i=0;i<100;i++){
employee = new Employee();
employee.setName("test"+i);
employee.setAge(100 - i);
employees.add(employee);
}
employeeCrudRepository.save(employees);
}
}
5-2 PagingAndSortingRespository接口使用详解
PagingAndSortingRespository接口使用详解
该接口包含分页和排序的功能
带排序的查询:findAll(Sort sort)
带排序的分页查询:findAll(Pageable pageable)
代码演示:
1、编写EmployeePagingAndSortingRepository接口
package com.myimooc.springdata.jpa.repository;
import com.myimooc.springdata.jpa.domain.Employee;
import org.springframework.data.repository.PagingAndSortingRepository;
/**
* 使用 PagingAndSortingRepository 实现分页和排序功能
* Created by ZC on 2017/4/26.
*/
public interface EmployeePagingAndSortingRepository extends PagingAndSortingRepository<Employee,Integer> {
}
2、编写EmployeePagingAndSortingRepositoryTest单元测试类
package com.myimooc.springdata.jpa.repository;
import com.myimooc.springdata.jpa.config.SpringConfig;
import com.myimooc.springdata.jpa.domain.Employee;
import com.myimooc.springdata.jpa.repository.EmployeePagingAndSortingRepository;
import com.myimooc.springdata.jpa.service.EmployeeService;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import java.util.ArrayList;
import java.util.List;
/**
* PagingAndSortingRepository 单元测试类
* Created by ZC on 2017/4/26.
*/
public class EmployeePagingAndSortingRepositoryTest {
private ApplicationContext ctx = null;
private EmployeePagingAndSortingRepository employeePagingAndSortingRepository = null;
@Before
public void init(){
ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
employeePagingAndSortingRepository = ctx.getBean(EmployeePagingAndSortingRepository.class);
}
@After
public void destroy(){
ctx = null;
}
/**
* 分页功能测试
*/
@Test
public void pageTest(){
// page: index是从0开始的,不是从1开始的
Pageable pageable = new PageRequest(0,9);
Page<Employee> employeePage = employeePagingAndSortingRepository.findAll(pageable);
System.out.println("查询的总页数:"+employeePage.getTotalPages());
System.out.println("查询的总记录数:"+employeePage.getTotalElements());
System.out.println("查询的当前第几页:"+(employeePage.getNumber() + 1));
System.out.println("查询的当前页面的集合:"+employeePage.getContent());
System.out.println("查询的当前页面的记录数:"+employeePage.getNumberOfElements());
}
/**
* 分页和排序功能测试
*/
@Test
public void pageAndSort(){
Sort.Order order = new Sort.Order(Sort.Direction.ASC,"id");
Sort sort = new Sort(order);
// page: index是从0开始的,不是从1开始的
Pageable pageable = new PageRequest(0,5,sort);
Page<Employee> employeePage = employeePagingAndSortingRepository.findAll(pageable);
System.out.println("查询的总页数:"+employeePage.getTotalPages());
System.out.println("查询的总记录数:"+employeePage.getTotalElements());
System.out.println("查询的当前第几页:"+(employeePage.getNumber() + 1));
System.out.println("查询的当前页面的集合:"+employeePage.getContent());
System.out.println("查询的当前页面的记录数:"+employeePage.getNumberOfElements());
}
}
5-3 JpaRepository接口使用详解
JpaRepository接口使用详解
finaAll:查询所有记录
findAll(Sort sort):查询所有记录并排序
save(entities):保存多个实体对象
fiush:
deleteInBatch(entities):一个批次里面删除那些实体
代码演示:
1、编写EmployeeJpaRepository接口
package com.myimooc.springdata.jpa.repository;
import com.myimooc.springdata.jpa.domain.Employee;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.repository.PagingAndSortingRepository;
/**
* 使用 JpaRepository 接口
* Created by ZC on 2017/4/26.
*/
public interface EmployeeJpaRepository extends JpaRepository<Employee,Integer> {
}
2、编写EmployeeJpaRepositoryTest单元测试类
package com.myimooc.springdata.jpa.repository;
import com.myimooc.springdata.jpa.config.SpringConfig;
import com.myimooc.springdata.jpa.domain.Employee;
import com.myimooc.springdata.jpa.repository.EmployeeJpaRepository;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.repository.PagingAndSortingRepository;
/**
* EmployeeJpaRepository 单元测试类
* Created by ZC on 2017/4/26.
*/
public class EmployeeJpaRepositoryTest {
private ApplicationContext ctx = null;
private EmployeeJpaRepository employeeJpaRepository = null;
@Before
public void init(){
ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
employeeJpaRepository = ctx.getBean(EmployeeJpaRepository.class);
}
@After
public void destroy(){
ctx = null;
}
@Test
public void findTest(){
Employee employee = employeeJpaRepository.findOne(99);
System.out.println("employee"+employee.toString());
System.out.println("employee(10)"+employeeJpaRepository.exists(10));
System.out.println("employee(102)"+employeeJpaRepository.exists(102));
}
}
5-4 JpaSpecificationExecutor接口使用详解
JpaSpecificationExecutor接口使用详解
Specification封装了JPA Criteria查询条件
代码演示:
1、编写EmployeeJpaSpecificationExecutor接口
package com.myimooc.springdata.jpa.repository;
import com.myimooc.springdata.jpa.domain.Employee;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
/**
* 使用 JpaSpecificationExecutor 接口
* Created by ZC on 2017/4/26.
*/
public interface EmployeeJpaSpecificationExecutor extends JpaRepository<Employee,Integer> ,
JpaSpecificationExecutor<Employee>{
}
2、编写EmployeeJpaSpecificationExecutorTest单元测试类
package com.myimooc.springdata.jpa.repository;
import com.myimooc.springdata.jpa.config.SpringConfig;
import com.myimooc.springdata.jpa.domain.Employee;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import javax.persistence.criteria.*;
/**
* EmployeeJpaSpecificationExecutor 单元测试类
* Created by ZC on 2017/4/26.
*/
public class EmployeeJpaSpecificationExecutorTest {
private ApplicationContext ctx = null;
private EmployeeJpaSpecificationExecutor employeeJpaSpecificationExecutor = null;
@Before
public void init(){
ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
employeeJpaSpecificationExecutor = ctx.getBean(EmployeeJpaSpecificationExecutor.class);
}
@After
public void destroy(){
ctx = null;
}
/**
* 1、分页
* 2、排序
* 3、查询条件:age > 50
*/
@Test
public void queryTest(){
Sort.Order order = new Sort.Order(Sort.Direction.DESC,"id");
Sort sort = new Sort(order);
// page: index是从0开始的,不是从1开始的
Pageable pageable = new PageRequest(0,5,sort);
/**
* root : 就是我们要查询的类型 (Employee)
* query : 添加查询条件
* cb : 构建 Predicate
*/
Specification<Employee> specification = new Specification<Employee>() {
// 查询条件
public Predicate toPredicate(Root<Employee> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
// root (employee (age))
Path path = root.get("age");
return cb.gt(path,50);
}
};
Page<Employee> employeePage = employeeJpaSpecificationExecutor.findAll(specification,pageable);
System.out.println("查询的总页数:"+employeePage.getTotalPages());
System.out.println("查询的总记录数:"+employeePage.getTotalElements());
System.out.println("查询的当前第几页:"+(employeePage.getNumber() + 1));
System.out.println("查询的当前页面的集合:"+employeePage.getContent());
System.out.println("查询的当前页面的记录数:"+employeePage.getNumberOfElements());
}
}
第六章:课程总结
6-4 课程总结
课程总结
Spring Data概览
传统方式访问数据库
Spring Data快速起步
Spring Data JPA进阶
Spring Data JAP高级
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。