简述:
Spring Data是Spring的一个子项目, 用于访问数据库
其主要目标是使得数据库的访问变得方便快捷
Spring Data中已经提供了常用的一些接口和实现类, 比如分页、排序、DAO一些常用的操作
从而我们可利用spring data快速构建项目
导入spring data的jar包:
中心仓库去下载
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-jpa -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>2.0.2.RELEASE</version>
</dependency>
根据不同DB还导一个数据库驱动
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.14</version>
</dependency>
核心接口:
Repository接口是spring data提供的核心接口, 它是一个空接口(标记接口)
package org.springframework.data.repository;
import java.io.Serializable;
public interface Repository<T, ID extends Serializable> {
}
基础的 Repository 只提供了最基本的数据访问功能, 其子接口扩展了其功能
Repository: 顶级接口, 仅仅是一个标识,表示任何继承它的接口都是仓库接口类
CrudRepository: 继承 Repository,实现了一组 CRUD(增删改查) 相关的方法
PagingAndSortingRepository: 继承 CrudRepository,实现了一组 分页排序 相关的方法
JpaRepository: 继承 PagingAndSortingRepository,实现一组 JPA 规范 相关的方法
Repository在commons包中, JpaRepostory在data包r中 , 如下
默认存在的一些方法:
我们定义一个接口MyRespository, 继承了JpaRespository, 则可以直接使用如下的方法
repository为MyRespository的对象
repository.findAll();
repository.findOne(xxx);
repository.save();
repository.delete(xxx);
repository.count();
repository.exists();
...
// 查询所有对象
@RequestMapping("/findAll")
public void findAll() {
List<Movie> movies = dao.findAll();
for (Movie movie : movies) {
System.out.println(movie.getDesc());
}
}
// 查询某个对象
@RequestMapping("/findOne/{id}")
public void findOne(@PathVariable String id) {
int parseId = Integer.parseInt(id);
Movie movie = dao.findOne(parseId);
if (movie == null) {
return;
}
System.out.println(movie.getDesc());
}
// 删除对象
@RequestMapping("/delete/{id}")
public void delete(@PathVariable String id) {
int parseId = Integer.parseInt(id);
boolean flag = false;
try {
dao.delete(parseId);
flag = true;
} catch (Exception e) {}
if (flag == true) {
System.out.println("删除成功");
} else {
System.out.println("删除失败");
}
}
// count
@RequestMapping("/count")
public void count() {
System.out.println(dao.count());
}
// exists
@RequestMapping("/exists/{id}")
public void exists(@PathVariable String id) {
int parseId = Integer.parseInt(id);
System.out.println(dao.exists(parseId));
}
// save
@RequestMapping("/save") //注意数据表不可使用关键字, 否则会提示sql错误
public void save(String name, String img_url, String year, String artist, String desc) {
Movie entity = new Movie();
entity.setName(name);
entity.setImgUrl(img_url);
entity.setYear(year);
entity.setArtist(artist);
entity.setDesc(desc);
entity.setId(11);
try {
dao.save(entity);
dao.flush();
System.out.println("写入成功");
} catch (Exception e) {
System.out.println("写入失败");
}
}
方法定义规范:
符合规范的方法,可以不用写实现
设计查询方法以find 或 read 或 get开头
设计 条件查询 时,条件用属性名连接, 例如如下查询
先继承JpaRepository, 再写自己的方法(比如这里的 findById, 也可以改为 getById / readById)
我并没有写 这个MovieDao接口的实现, 但是在Controllrt中就可以使用(因为我遵循了JPA规范, 所以不用写实现)
spring关键字查询
就是表明查询的逻辑, 比如getByNameAndYear (根据电影名称和年份查询)
这个and就是关键字
添加如下的一个方法
同样没有实现, 在Controller中也可直接使用
接口中我写的JPA规范方法(全部有效)
public interface MovieDao extends JpaRepository<Movie, Integer> {
// 根据电影的id查询, findById -- 属性名为id
public Movie getById(Integer id);
// 根据电影名称和电影描述查询
public Movie getByNameAndYear(String name, String year);
// Or, 这可能查询出多个结果, 所以需要用集合保存结果
public List<Movie> findByNameOrYear(String name, String year);
// Between, 从第一个参数到第二个参数之间
public List<Movie> findByYearBetween(String start, String end);
// LessThan, 小于
public List<Movie> findByYearLessThan(String data);
// GreaterThan, 大于
public List<Movie> readByYearGreaterThan(String data);
// After, 字段中大于data的记录(测试时效果与GreaterThan相同, 不知道有什么区别)
public List<Movie> readByYearAfter(String data);
// Before, 字段中小于data的记录(同上)
public List<Movie> readByYearBefore(String data);
// IsNull, 说明null表示没有值, 它并不是空值, 空值是: "" (空字符串), 查询时需要注意它们的区别
public List<Movie> getByYearIsNull();
// IsNotNull
public List<Movie> getByYearIsNotNull();
// like(模糊查询 like为表达式, 具体由Cotroller中拼接)
public List<Movie> findByDescLike(String like);
// In, in表示从列表中查询, 参数可以是数组和集合
public List<Movie> findByYearIn(String[] s);
// 按Desc模糊查询再按Year降序排列 /*这个是比较综合的查询*/
public List<Movie> findTop3ByDescLikeOrderByYearDesc(String like);
}
Controller中我对接口的测试 (只看方法名称就知道代表的意思)
@RestController
public class MovieController {
@Autowired
private MovieDao dao;
@RequestMapping("/getMovieById/{id}")
public void getMovieById(@PathVariable String id) {
int parseId = 0;
try {
parseId = Integer.parseInt(id);
} catch (NumberFormatException e) {
System.err.println("请输入非零正数");
return;
}
/*如果传入的参数正确才查询DB*/
if (parseId > 0) {
Movie movie = dao.getById(parseId);
if (movie != null) {
System.out.println(movie.getName());
} else {
System.out.println("您查询的内容不存在");
}
return;
}
System.out.println("请输入非零正数");
}
@RequestMapping("/findMovieByNameAndYear")
public String getByNameAndYear(String name, String year) {
if (year == null || name == null) {
System.out.println("请输入名称和年份");
return null;
}
Movie movie = dao.getByNameAndYear(name, year);
if (movie != null) {
System.out.println(movie.getDesc());
}
return null;
}
@RequestMapping("/findMovieOrNameAndYear")
public void getByNameOrYear(String name, String year) {
List<Movie> movies = dao.findByNameOrYear(name, year);
for (Movie movie : movies) {
System.out.println(movie.getDesc());
}
}
@RequestMapping("/findByYearBetween")
// between
public void findByYearBetween(String start , String end) {
if (start == null || end == null) {
return;
}
List<Movie> movies = dao.findByYearBetween(start, end);
for (Movie movie : movies) {
System.out.println(movie.getDesc());
}
}
@RequestMapping("/findByYearLessThan")
public void findByYearLessThan(String data) {
if (data == null) {
return;
}
List<Movie> movies = dao.findByYearLessThan(data);
for (Movie movie : movies) {
System.out.println(movie.getDesc());
}
}
@RequestMapping("/readByYearGreaterThan")
public void readByYearGreaterThan(String data) {
if (data == null) {
return;
}
List<Movie> movies = dao.readByYearGreaterThan(data);
for (Movie movie : movies) {
System.out.println(movie.getDesc());
}
}
@RequestMapping("/readByYearAfter")
// After
public void readByYearAfter(String data) {
if (data == null) {
return;
}
List<Movie> movies = dao.readByYearAfter(data);
for (Movie movie : movies) {
System.out.println(movie.getDesc());
}
}
@RequestMapping("/getByYearIsNull")
public void getByYearIsNull() {
List<Movie> movies = dao.getByYearIsNull();
for (Movie movie : movies) {
System.out.println(movie.getDesc());
}
}
@RequestMapping("/getByYearIsNotNull")
public void getByYearIsNotNull() {
List<Movie> movies = dao.getByYearIsNotNull();
for (Movie movie : movies) {
System.out.println(movie.getDesc());
}
}
@RequestMapping("/findByDescLike")
public void findByDesc(String data) {
String like = "%" + data + "%";
List<Movie> movies = dao.findByDescLike(like);
System.out.println(movies.size());
}
// 模糊查询排序(JPA规范看: 按Desc查询, 按Year排序)
@RequestMapping("/findTop3ByDescLikeOrderByYearDesc")
public void findTop3ByDescLikeOrderByYearDesc(String data) {
String like = "%" + data + "%";
List<Movie> movies = dao.findTop3ByDescLikeOrderByYearDesc(like);
for (Movie movie : movies) {
System.out.println(movie.getYear() + ": " + movie.getName());
}
}
@RequestMapping("/findByYearIn")
public void findByYearIn(String a) {
String[] arr = null;
if(a != null) {
arr = a.split(",");
}
if(arr == null) return;
List<Movie> movies = dao.findByYearIn(arr);
for (Movie movie : movies) {
System.out.println(movie.getName());
}
}
// 查询所有对象
@RequestMapping("/findAll")
public void findAll() {
List<Movie> movies = dao.findAll();
for (Movie movie : movies) {
System.out.println(movie.getDesc());
}
}
// 查询某个对象
@RequestMapping("/findOne/{id}")
public void findOne(@PathVariable String id) {
int parseId = Integer.parseInt(id);
Movie movie = dao.findOne(parseId);
if (movie == null) {
return;
}
System.out.println(movie.getDesc());
}
// 删除对象
@RequestMapping("/delete/{id}")
public void delete(@PathVariable String id) {
int parseId = Integer.parseInt(id);
boolean flag = false;
try {
dao.delete(parseId);
flag = true;
} catch (Exception e) {}
if (flag == true) {
System.out.println("删除成功");
} else {
System.out.println("删除失败");
}
}
// count
@RequestMapping("/count")
public void count() {
System.out.println(dao.count());
}
// exists
@RequestMapping("/exists/{id}")
public void exists(@PathVariable String id) {
int parseId = Integer.parseInt(id);
System.out.println(dao.exists(parseId));
}
// save
@RequestMapping("/save")
public void save(String name, String img_url, String year, String artist, String desc) {
Movie entity = new Movie();
entity.setName(name);
entity.setImgUrl(img_url);
entity.setYear(year);
entity.setArtist(artist);
entity.setDesc(desc);
try {
dao.save(entity);
System.out.println("写入成功");
} catch (Exception e) {
System.out.println("写入失败");
e.printStackTrace();
}
}
}
Example查询
count(), findAll(),exists()的参数都可以使用Example对象(来实现模糊查询)
1.先创建Example
2.再创建ExampleMatcher对象
3.使用count, findAll. exists方法
@RequestMapping("/exampleSelect")
public void exampleCount(String name, String year) {
// 创建对象
Movie movie = new Movie();
movie.setName(name);
movie.setYear(year);
//创建匹配器
ExampleMatcher matcher = ExampleMatcher.matching()
// name, year代表数据表中的字段名(但以实体类中的字段名为准)
.withMatcher("name", GenericPropertyMatchers.contains()) // 包含
.withMatcher("year", GenericPropertyMatchers.endsWith()) // 以结尾
//.withMatcher("实体字段名", 匹配条件); // 还可以继续加匹配
// 要忽略id(不清楚原因)
.withIgnorePaths("id");
//创建实例, 用这个实例movie去匹配数据表中的每条记录 (这个实例对象就相当于一条数据表记录)
Example<Movie> ex = Example.of(movie, matcher);
long mo = dao.count(ex);
System.out.println(mo);
}
JPQL查询
类似HQL语法在方法上使用@Query注解
@Query(value="select * from movie where id = ?1", nativeQuery = true)
public Movie findDefinedId(int id);
上面在Query注解中加上了nativeQuery = true, 则这就是原生sql查询
如下的字段名id, movie_name只能是原数据表中的字段名, 不能改为实体类中的属性名
查询单个字段, 返回简单类型
@Query(value="select id, movie_name from movie where id = ?1", nativeQuery = true)
public String findDefinedId(int id);
查询多个字段, 返回数组类型
//原生sql查询
@Query(value="select movie_name, id, artist from movie where id = ?1", nativeQuery = true)
public String[] findDefinedId(int id);
查询全部字段可以返回对象
HQL查询也是使用Query注解, 但是不加nativeQuery=true
// HQL查询, 如果查询的是单个字段, 就返回简单类型
// HQL查询, 如果查询的是多个字体, 则返回的是数组
@Query(value="select id, name from Movie where id = ?1")
public String[] findDefinedHqlId(Long id);
// HQL查询, 查询多个字段, 但是想要返回对象, 则应该这样写, 但是要保证实体中有引构造器
@Query(value="select new Movie(id, name) from Movie where id = ?1")
public List<Movie> findDefinedHqlIdToObject(Long id);
使用HQL查询, 要当作类来使用, 比如这里的Movie必是类, 而不是表名(所以大写)
这里的id, name也是属性名, 而不是表中的字段名
还有这里的参数Long id, 不可使用int型, 因为我的id在实体类中就是定义的long型, 而不是int型
这些千万要注意区别, 否则会出现类型不能转换的异常
多表关联查询, 对于业务复杂的HQL性能很差, 而使用原生sql查询, 效率更佳
但是解析查询的结果比较麻烦一点
接口:
// 多表原生联查, 返回的必须是List<Object>类型
@Query(nativeQuery=true, value="select S.stitle, S.description, M.movie_name, M.year from movie M join section S on S.sid = M.id where M.year > 1997 order by M.year")
public List<Object> unionTable();
控制层:
@RequestMapping("/unionTable")
public void unionTable() {
// 这个集合中的Object对象 本质是对象数组
List<Object> objs = dao.unionTable();
for (Object obj : objs) {
// 因为拿到的对象本质是对象数组, 所以可强制转换为对象数组类型
Object[] str = (Object[])obj;
// 通过数组的下标, 将数组中的数据放入vo对象中
MovieSection ms = new MovieSection();
ms.setTitle((String)str[0]);
ms.setDescription((String)str[1]);
ms.setMovie_name((String)str[2]);
ms.setYear((String)str[3]);
// 还可以将这些对象放入List中, 再用@ResponseBody返回
}
}
List<Object>的真实结构如下: (外层一个集合, 中间三个对象数组, 最里面就是单个的String对象)
[ ['a', 'b','c'], ['1', '2', '3'], ['盗梦空间', '排行9', '2001年' ] ]
另外还有参数点位符等, spring data 的大致内容就是这些