什么是Spring Data?
- 主旨:provide a familiar and consistent,Spring-based programming model for data access
- 历史:2010,作者
Rod Johnso
,Spring Source 项目- 官网:spring data
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高级
传统方式访问数据库
- JDBC
- Spring JdbcTemplate
- 弊端分析
其一:传统方式访问数据库之JDBC
- Connection
- Statement
- ResultSet
- Test Case
JDBC Demo
- 创建Maven项目
maven工程主要配置文件pom.xml,用来配置项目
添加依赖
<!--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>
2.数据表准备
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",20);
insert into student(name,age) values("wangwu",20);
3.开发JDBCUtil工具类
获取Connection,关闭Connection、Statement、ResultSet
注意事项:配置的属性放在配置文件中,然后通过代码的方式将配置文件中的数据加载进来即可。
***JDBCUtil.java ***
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
/**
* JDBC工具类:
* 1)获取Connection
* 2)释放资源
*/
public class JDBCUtil {
/**
* 获取Connection
* @return 所获得到的JDBC的Connection
*/
public static Connection getConnection() throws Exception {
/*String url = "jdbc:mysql://localhost:3306/spring_data";
String user = "root";
String password = "root";
String driverClass = "com.mysql.jdbc.Driver";*/
InputStream inputStream = JDBCUtil.class.getClassLoader().getResourceAsStream("db.properties");
Properties properties = new Properties();
properties.load(inputStream);
String url = properties.getProperty("jdbc.url");
String user = properties.getProperty("jdbc.user");
String password = properties.getProperty("jdbc.password");
String driverClass = properties.getProperty("jdbc.driverClass");
Class.forName(driverClass);
Connection connection = DriverManager.getConnection(url,user,password);
return 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();
}
}
}
}
db.properties
jdbc.url = jdbc:mysql://localhost:3306/spring_data
jdbc.user = root
jdbc.password = root
jdbc.driverClass = com.mysql.jdbc.Driver
4.domain-->dao-->daoImpl
Student.java
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;
}
}
StudentDao.java
public interface StudentDao {
/**
* 查询所有学生
* @return
*/
public List<Student> query();
/**
* 添加学生
* @param student
*/
public void save(Student student);
}
StudentDaoImpl.java
public class StudentDaoImpl implements StudentDao {
@Override
public List<Student> query() {
List<Student> students = new ArrayList<Student>();
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
String sql = "select id,name,age from student";
try {
connection = JDBCUtil.getConnection();
preparedStatement = connection.prepareStatement(sql);
resultSet = preparedStatement.executeQuery();
Student student = null;
while(resultSet.next()){
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
int age = resultSet.getInt("age");
student = new Student();
student.setId(id);
student.setName(name);
student.setAge(age);
students.add(student);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtil.release(resultSet,preparedStatement,connection);
}
return students;
}
@Override
public void save(Student student) {
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
String sql = "insert into student(name,age) values(?,?)";
try {
connection = JDBCUtil.getConnection();
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1,student.getName());
preparedStatement.setInt(2,student.getAge());
preparedStatement.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtil.release(resultSet,preparedStatement,connection);
}
}
}
以上属于使用传统方式访问数据库之JDBC,紧接来使用Spring JdbcTemplate来访问数据库
其二:传统方式访问数据库之Spring JdbcTemplate
- Maven依赖
- DataSource & JdbcTemplate注入
- Test Case
使用Spring jdbc的方式操作数据库
- 添加依赖
<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.2.0.RELEASE</version>
</dependency>
- 配置beans.xml
- 开发spring jdbc版本的query和save方法
beans.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id = "dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
<property name="url" value="jdbc:mysql://localhost:3306/spring_data"/>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="studentDao" class="com.imooc.dao.StudentDaoSpringJdbcTemplate">
<property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean>
</beans>
StudentDaoSpringJdbcTemplate.java
public class StudentDaoSpringJdbcTemplate implements StudentDao{
private JdbcTemplate jdbcTemplate;
@Override
public List<Student> query() {
List<Student> students = new ArrayList<Student>();
String sql = "select id,name,age from student";
jdbcTemplate.query(sql, new RowCallbackHandler() {
@Override
public void processRow(ResultSet resultSet) throws SQLException {
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
int age = resultSet.getInt("age");
Student student = new Student();
student.setId(id);
student.setName(name);
student.setAge(age);
students.add(student);
}
});
return students;
}
@Override
public void save(Student student) {
String sql = "insert into student(name,age) values(?,?)";
jdbcTemplate.update(sql,new Object[]{student.getName(),student.getAge()});
}
public JdbcTemplate getJdbcTemplate() {
return jdbcTemplate;
}
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
}
以上属于使用传统方式访问数据库之Spring JdbcTemplate
以上两种传统方式访问数据库的弊端分析:
- DAO has many many code
- DAOImpl has many duplicate code
- Develop the page and other functions
- ...
Spring Data JPA
- 开发环境
- Spring Data JPA HelloWorld开发
Spring Data JPA进阶
Repository接口讲解
- Repository接口是Spring Data的核心接口,不提供任何方法
- public interface Repository<T,ID extends Serializable>{}
- @RepositoryDefinition注解的使用
Repository类的定义(源码):
public interface Repository<T, ID extends Serializable> {
}
1. Repository是一个空接口,标记接口,没有包含方法声明的接口
2. 如果我们定义的接口EmployeeRepository extends Repository
如果我们的接口没有extends Repository,运行时会报错
org.springframework.beans.factory.NoSuchBeanDefinitionException:No qualifying bean of type 'com.imooc.repository.EmployeeRepository' available
3. 添加注解能够达到不用extends Repository的功能
@RepositoryDefinition(domainClass = Employee.class,idClass = Integer.class)
#####Repository的子接口讲解
> * CrudRepository:继承Repository,实现了CRUD相关的方法
public interface CrudRepository<T, ID extends Serializable> extends Repository<T, ID> {
/**
* Saves a given entity. Use the returned instance for further operations as the save operation might have changed the
* entity instance completely.
*
* @param entity
* @return the saved entity
*/
<S extends T> S save(S entity);
/**
* Saves all given entities.
*
* @param entities
* @return the saved entities
* @throws IllegalArgumentException in case the given entity is (@literal null}.
*/
<S extends T> Iterable<S> save(Iterable<S> entities);
/**
* Retrieves an entity by its id.
*
* @param id must not be {@literal null}.
* @return the entity with the given id or {@literal null} if none found
* @throws IllegalArgumentException if {@code id} is {@literal null}
*/
T findOne(ID id);
/**
* Returns whether an entity with the given id exists.
*
* @param id must not be {@literal null}.
* @return true if an entity with the given id exists, {@literal false} otherwise
* @throws IllegalArgumentException if {@code id} is {@literal null}
*/
boolean exists(ID id);
/**
* Returns all instances of the type.
*
* @return all entities
*/
Iterable<T> findAll();
/**
* Returns all instances of the type with the given IDs.
*
* @param ids
* @return
*/
Iterable<T> findAll(Iterable<ID> ids);
/**
* Returns the number of entities available.
*
* @return the number of entities
*/
long count();
/**
* Deletes the entity with the given id.
*
* @param id must not be {@literal null}.
* @throws IllegalArgumentException in case the given {@code id} is {@literal null}
*/
void delete(ID id);
/**
* Deletes a given entity.
*
* @param entity
* @throws IllegalArgumentException in case the given entity is (@literal null}.
*/
void delete(T entity);
/**
* Deletes the given entities.
*
* @param entities
* @throws IllegalArgumentException in case the given {@link Iterable} is (@literal null}.
*/
void delete(Iterable<? extends T> entities);
/**
* Deletes all entities managed by the repository.
*/
void deleteAll();
}
> * PagingAndSortingRepository:继承CrudRepository,实现了分页排序相关的方法
public interface PagingAndSortingRepository<T, ID extends Serializable> extends CrudRepository<T, ID> {
/**
* Returns all entities sorted by the given options.
*
* @param sort
* @return all entities sorted by the given options
*/
Iterable<T> findAll(Sort sort);
/**
* Returns a {@link Page} of entities meeting the paging restriction provided in the {@code Pageable} object.
*
* @param pageable
* @return a page of entities
*/
Page<T> findAll(Pageable pageable);
}
> * JpaRepository:继承PagingAndSortingRepository,实现JPA规范相关的方法
public interface JpaRepository<T, ID extends Serializable> extends PagingAndSortingRepository<T, ID> {
/*
* (non-Javadoc)
* @see org.springframework.data.repository.CrudRepository#findAll()
*/
List<T> findAll();
/*
* (non-Javadoc)
* @see org.springframework.data.repository.PagingAndSortingRepository#findAll(org.springframework.data.domain.Sort)
*/
List<T> findAll(Sort sort);
/*
* (non-Javadoc)
* @see org.springframework.data.repository.CrudRepository#findAll(java.lang.Iterable)
*/
List<T> findAll(Iterable<ID> ids);
/*
* (non-Javadoc)
* @see org.springframework.data.repository.CrudRepository#save(java.lang.Iterable)
*/
<S extends T> List<S> save(Iterable<S> entities);
/**
* Flushes all pending changes to the database.
*/
void flush();
/**
* Saves an entity and flushes changes instantly.
*
* @param entity
* @return the saved entity
*/
<S extends T> S saveAndFlush(S entity);
/**
* Deletes the given entities in a batch which means it will create a single {@link Query}. Assume that we will clear
* the {@link javax.persistence.EntityManager} after the call.
*
* @param entities
*/
void deleteInBatch(Iterable<T> entities);
/**
* Deletes all entites in a batch call.
*/
void deleteAllInBatch();
/**
* Returns a reference to the entity with the given identifier.
*
* @param id must not be {@literal null}.
* @return a reference to the entity with the given identifier.
* @see EntityManager#getReference(Class, Object)
*/
T getOne(ID id);
}
#####Repository中查询方法定义规则和使用
> * 了解Spring Data中查询方法名称的定义规则
* 使用Spring Data完成复杂查询方法名称的命名
![Repository中查询方法定义规则和使用](http://upload-images.jianshu.io/upload_images/3831072-c4d28f7294210447.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
![Repository中查询方法定义规则和使用](http://upload-images.jianshu.io/upload_images/3831072-68d927a438c5fd36.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
***beans.xml***
<?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">
<!--1. 配置数据源-->
<bean id = "dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
<property name="url" value="jdbc:mysql://localhost:3306/spring_data"/>
</bean>
<!--2.配置EntityManager-->
<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>
<!--3.配置事务管理器-->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<!--4.支持注解的事务-->
<tx:annotation-driven transaction-manager="transactionManager"/>
<!--5.配置spring data-->
<jpa:repositories base-package="com.imooc" entity-manager-factory-ref="entityManagerFactory"/>
<context:component-scan base-package="com.imooc"/>
</beans>
***Employee.java***
/**
雇员:先开发实体类====>自动生成数据表
-
Created by ZCF on 2017/6/21.
*/
@Entity
public class Employee {private Integer id;
private String name;
private Integer age;
@GeneratedValue
@Id
public Integer getId() {
return id;
}public void setId(Integer id) {
this.id = id;
}@Column(length = 20)
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;
}
}
***EmployeeRepository.java***
@RepositoryDefinition(domainClass = Employee.class,idClass = Integer.class)
public interface EmployeeRepository{
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);
}
***EmployeeRepositoryTest.java***
public class EmployeeRepositoryTest {
private ApplicationContext ctx = null;
private EmployeeRepository employeeRepository = null;
@Before
public void setup(){
ctx = new ClassPathXmlApplicationContext("beans-new.xml");
employeeRepository = ctx.getBean(EmployeeRepository.class);
System.out.println("setup");
}
@After
public void tearDown(){
ctx = null;
System.out.println("tearDown");
}
@Test
public void testFindByName(){
Employee employee = employeeRepository.findByName("zhangsan");
System.out.println("id:" + employee.getId() + ",name:" + employee.getName() + ",age:" + employee.getAge());
}
@Test
public void testfindByNameStartingWithAndAgeLessThan(){
List<Employee> employees = employeeRepository.findByNameStartingWithAndAgeLessThan("test",22);
for(Employee employee:employees){
System.out.println("id:" + employee.getId() + ",name:" + employee.getName() + ",age:" + employee.getAge());
}
}
@Test
public void testfindByNameEndingWithAndAgeLessThan(){
List<Employee> employees = employeeRepository.findByNameEndingWithAndAgeLessThan("6",23);
for(Employee employee:employees){
System.out.println("id:" + employee.getId() + ",name:" + employee.getName() + ",age:" + employee.getAge());
}
}
@Test
public void testfindByNameInOrAgeLessThan(){
List<String> names = new ArrayList<String>();
names.add("test1");
names.add("test2");
names.add("test3");
List<Employee> employees = employeeRepository.findByNameInOrAgeLessThan(names,22);
for(Employee employee:employees){
System.out.println("id:" + employee.getId() + ",name:" + employee.getName() + ",age:" + employee.getAge());
}
}
@Test
public void testfindByNameInAndAgeLessThan(){
List<String> names = new ArrayList<String>();
names.add("test1");
names.add("test2");
names.add("test3");
List<Employee> employees = employeeRepository.findByNameInAndAgeLessThan(names,22);
for(Employee employee:employees){
System.out.println("id:" + employee.getId() + ",name:" + employee.getName() + ",age:" + employee.getAge());
}
}
}
#####Repository
> * Repository
* RepositoryDefinition
* Repository Query Specifications
* Query Annotation
* Update/Delete/Transaction
#####Repository Hierachy
> * CrudRepository
* PagingAndSortingRespository
* JpaRepository
* JpaSpecificationExecutor
***Spring Data 弊端分析***
> 1. 方法名会比较长:约定大于配置
2. 对于一些复杂的查询,是很难实现
#####Query注解使用
> * 在Repository方法中使用,不需要遵循查询方法命名规则
* 只需要将@Query定义在Repository中的方法之上即可
* 命名参数及索引参数的使用
* 本地查询
@Query("select o from Employee o where id = (select max(id) from Employee t1) ")
public Employee getEmployeeByMaxId();
@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);
@Query("select o from Employee o where o.name like %?1%")
public List<Employee> queryLike1(String name);
@Query("select o from Employee o where o.name like %:name%")
public List<Employee> queryLike2(@Param("name") String name);
@Query(nativeQuery = true,value = "select count(1) from employee")
public long getCount();
#####更新及删除操作整合事务的使用
> * @Modifying注解使用
* @Modifying结合@Query注解执行更新操作
* @Transactional在Spring Data中的使用
###事务在Spring data中的使用:
> * 事务一般是在Service层
* @Query、@Modifying、@Transactional的综合使用
***EmployeeRepository.java***
@Modifying
@Query("update Employee o set o.age = :age where o.id = :id")
public void update(@Param("id") Integer id,@Param("age") Integer age);
***EmployeeService .java***
@Service
public class EmployeeService {
@Autowired
private EmployeeRepository employeeRepository;
@Transactional
public void update(Integer id,Integer age){
employeeRepository.update(id,age);
}
}
###CrudRepository接口使用详解
> * save(entity)
* save(entities)
* findOne(id)
* exists(id)
* findAll()
* delete(id)
* delete(entity)
* delete(entities)
* deleteAll()
***EmployeeCrudRepository .java***
public interface EmployeeCrudRepository extends CrudRepository<Employee,Integer> {
}
***EmployeeService .java***
@Service
public class EmployeeService {
@Autowired
private EmployeeRepository employeeRepository;
@Autowired
private EmployeeCrudRepository employeeCrudRepository;
@Transactional
public void update(Integer id,Integer age){
employeeRepository.update(id,age);
}
@Transactional
public void save(List<Employee> employees){
employeeCrudRepository.save(employees);
}
}
####PagingAndSortingRespository接口使用详解
> * 该接口包含分页和排序的功能
* 带排序的查询:findAll(Sort sort)
* 带排序的分页查询:findAll(Pageable pageable)
***EmployeePagingAndSortingRepository .java***
public interface EmployeePagingAndSortingRepository extends PagingAndSortingRepository<Employee,Integer> {
}
***EmployeePagingAndSortingRepositoryTest .java***
public class EmployeePagingAndSortingRepositoryTest {
private ApplicationContext ctx = null;
private EmployeePagingAndSortingRepository employeePagingAndSortingRepository = null;
@Before
public void setup(){
ctx = new ClassPathXmlApplicationContext("beans-new.xml");
employeePagingAndSortingRepository = ctx.getBean(EmployeePagingAndSortingRepository.class);
}
@After
public void tearDown(){
ctx = null;
System.out.println("tearDown");
}
@Test
public void testPage(){
//page:index从0开始
Pageable pageable = new PageRequest(0,5);
Page<Employee> page = employeePagingAndSortingRepository.findAll(pageable);
System.out.println(page.getTotalPages() + ":," + page.getNumber());
}
@Test
public void testPageAndSort(){
Sort.Order order = new Sort.Order(Sort.DEFAULT_DIRECTION.DESC,"id");
Sort sort = new Sort(order);
Pageable pageable = new PageRequest(0,5,sort);
Page<Employee> page = employeePagingAndSortingRepository.findAll(pageable);
System.out.println(page.getTotalPages() + ":," + page.getNumber());
}
}
####JpaRepository接口使用详解
> * findAll
* findAll(Sort sort)
* save(entities)
* flush
* deleteInBatch(entities)
***EmployeeJpaRepository ***
public interface EmployeeJpaRepository extends JpaRepository<Employee,Integer> {
}
####JpaSpecificationExecutor接口使用详解
> * Specification封装了JPA Criteria查询
**以上是根据慕课网SpringData视频学习的笔记**
[SpringData-慕课网](http://www.imooc.com/video/14540)
[代码-github](https://github.com/zcfGitHub/SpringData)