笔记65 Spring Boot快速入门(五)
SpringBoot+JPA
一、什么是JPA?
JPA是Java Persistence API的简称,中文名Java持久层API,是JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。真正干活的可能是Hibernate,TopLink等等实现了JPA规范的不同厂商,默认是Hibernate。
二、在springboot中使用JPA对数据库进行抽插
(一)目录结构
(二)pom.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4 <modelVersion>4.0.0</modelVersion> 5 6 <groupId>com.example</groupId> 7 <artifactId>springboot-jpa-demo</artifactId> 8 <version>0.0.1-SNAPSHOT</version> 9 <packaging>jar</packaging> 10 11 <name>springboot-jpa-demo</name> 12 <description>Demo project for Spring Boot</description> 13 14 <parent> 15 <groupId>org.springframework.boot</groupId> 16 <artifactId>spring-boot-starter-parent</artifactId> 17 <version>1.5.9.RELEASE</version> 18 <relativePath/> <!-- lookup parent from repository --> 19 </parent> 20 21 <properties> 22 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> 23 <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> 24 <java.version>1.8</java.version> 25 </properties> 26 27 <dependencies> 28 <dependency> 29 <groupId>org.springframework.boot</groupId> 30 <artifactId>spring-boot-starter-data-jpa</artifactId> 31 </dependency> 32 <dependency> 33 <groupId>org.springframework.boot</groupId> 34 <artifactId>spring-boot-starter-jdbc</artifactId> 35 </dependency> 36 <dependency> 37 <groupId>org.springframework.boot</groupId> 38 <artifactId>spring-boot-starter-tomcat</artifactId> 39 </dependency> 40 <dependency> 41 <groupId>org.springframework.boot</groupId> 42 <artifactId>spring-boot-starter-web</artifactId> 43 </dependency> 44 45 <dependency> 46 <groupId>mysql</groupId> 47 <artifactId>mysql-connector-java</artifactId> 48 <version>5.1.21</version> 49 </dependency> 50 <dependency> 51 <groupId>org.springframework.boot</groupId> 52 <artifactId>spring-boot-starter-test</artifactId> 53 <scope>test</scope> 54 </dependency> 55 56 <dependency> 57 <groupId>org.springframework.boot</groupId> 58 <artifactId>spring-boot-starter-thymeleaf</artifactId> 59 </dependency> 60 </dependencies> 61 62 <build> 63 <plugins> 64 <plugin> 65 <groupId>org.springframework.boot</groupId> 66 <artifactId>spring-boot-maven-plugin</artifactId> 67 </plugin> 68 </plugins> 69 </build> 70 71 72 </project>
1.数据库准备:创建表,插入数据等(略)
给出application.properties
1 spring.datasource.url=jdbc:mysql://127.0.0.1:3306/sh?characterEncoding=UTF-8 2 spring.datasource.username=root 3 spring.datasource.password=123456 4 spring.datasource.driver-class-name=com.mysql.jdbc.Driver 5 spring.jpa.properties.hibernate.hbm2ddl.auto=update 6 7 server.port=8080 8 server.context-path=/Test·
2.IDEA新建项目(列出比较重要的步骤)
3.新建pojo——Category
1 package com.example.springbootjpademo.pojo; 2 3 import javax.persistence.*; 4 5 @Entity 6 @Table(name = "category") 7 public class Category { 8 @Id 9 @GeneratedValue(strategy = GenerationType.IDENTITY) 10 @Column(name = "id") 11 private int id; 12 13 @Column(name = "name") 14 private String name; 15 public int getId() { 16 return id; 17 } 18 public void setId(int id) { 19 this.id = id; 20 } 21 public String getName() { 22 return name; 23 } 24 public void setName(String name) { 25 this.name = name; 26 } 27 }
4.创建DAO
创建dao接口CategoryDAO,继承了JpaRepository,并且提供泛型<Category,Integer> 表示这个是针对Category类的DAO,Integer表示主键是Integer类型。
JpaRepository 这个父接口,就提供了CRUD, 分页等等一系列的查询了,直接拿来用,都不需要二次开发的了。
1 package com.example.springbootjpademo.dao; 2 3 import com.example.springbootjpademo.pojo.Category; 4 import org.springframework.data.jpa.repository.JpaRepository; 5 6 public interface CategoryDAO extends JpaRepository<Category,Integer> { 7 }
5.创建Controller
自动加载CategoryDAO,然后调用findAll()查询所有条目。
1 package com.example.springbootjpademo.controller; 2 3 import com.example.springbootjpademo.dao.CategoryDAO; 4 import com.example.springbootjpademo.pojo.Category; 5 import org.springframework.beans.factory.annotation.Autowired; 6 import org.springframework.stereotype.Controller; 7 import org.springframework.ui.Model; 8 import org.springframework.web.bind.annotation.RequestMapping; 9 10 import java.util.List; 11 @Controller 12 public class CategoryController { 13 @Autowired 14 CategoryDAO categoryDAO; 15 16 @RequestMapping("/listCategory") 17 public String ListCategory(Model model) throws Exception{ 18 List<Category> categoryList=categoryDAO.findAll(); 19 model.addAttribute("categories",categoryList); 20 return "listCategory"; 21 } 22 }
6.创建view(使用Thymeleaf)
listCategory.html
1 <!DOCTYPE html> 2 <html lang="en" xmlns:th="http://www.thymeleaf.org"> 3 <head> 4 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 5 <title>Title</title> 6 </head> 7 <body> 8 <div class="showing"> 9 <h2>springboot+jpa</h2> 10 11 <table> 12 <thead> 13 <tr> 14 <th>id</th> 15 <th>name</th> 16 </tr> 17 </thead> 18 <tbody> 19 <tr th:each="c: ${categories}"> 20 <td align="center" th:text="${c.id}"></td> 21 <td align="center" th:text="${c.name}"></td> 22 </tr> 23 </tbody> 24 </table> 25 </div> 26 </body> 27 </html>
7.测试
三、总结(目前遇到的问题,可能有出入)
1.注意springboot自动扫描和装配的规则。
SpringBoot项目的Bean装配默认规则是根据Application类所在的包位置从上往下扫描!
“Application类”是指SpringBoot项目入口类。这个类的位置很关键:
如果Application类所在的包为:com.boot.app,则只会扫描com.boot.app包及其所有子包,如果service或dao所在包不在com.boot.app及其子包下,则不会被扫描!
即, 把Application类放到dao、service所在包的上级,com.boot.Application
2.jpa与2.0.3版本的springboot之间存在冲突,会导致项目出现问题。
3.JpaRepository 继承PagingAndSortingRepository,实现一组JPA规范相关的方法。
<1>什么是Repository
Repository(资源库):通过用来访问领域对象的一个类似集合的接口,在领域与数据映射层之间进行协调。这个叫法就类似于我们通常所说的DAO,在这里,我们就按照这一习惯把数据访问层叫Repository
Spring Data给我们提供几个Repository,基础的Repository提供了最基本的数据访问功能,其几个子接口则扩展了一些功能。它们的继承关系如下:
Repository: 仅仅是一个标识,表明任何继承它的均为仓库接口类,方便Spring自动扫描识别
CrudRepository: 继承Repository,实现了一组CRUD相关的方法
PagingAndSortingRepository: 继承CrudRepository,实现了一组分页排序相关的方法
JpaRepository: 继承PagingAndSortingRepository,实现一组JPA规范相关的方法
JpaSpecificationExecutor: 比较特殊,不属于Repository体系,实现一组JPA Criteria查询相关的方法
我们自己定义的XxxxRepository需要继承JpaRepository,这样我们的XxxxRepository接口就具备了通用的数据访问控制层的能力。
<2>CrudRepository接口
T save(T entity);//保存单个实体
Iterable<T> save(Iterable<? extends T> entities);//保存集合
T findOne(ID id);//根据id查找实体
boolean exists(ID id);//根据id判断实体是否存在
Iterable<T> findAll();//查询所有实体,不用或慎用!
long count();//查询实体数量
void delete(ID id);//根据Id删除实体
void delete(T entity);//删除一个实体
void delete(Iterable<? extends T> entities);//删除一个实体的集合
void deleteAll();//删除所有实体,不用或慎用!
<3>PagingAndSortingRepository接口
这个接口提供了分页与排序功能
Iterable<T> findAll(Sort sort);//排序
Page<T> findAll(Pageable pageable);//分页查询(含排序功能)
<4>JpaRepository接口
这个接口提供了JPA的相关功能
List<T> findAll();//查找所有实体
List<T> findAll(Sort sort);//排序 查找所有实体
List<T> save(Iterable<? extends T> entities);//保存集合
void flush();//执行缓存与数据库同步
T saveAndFlush(T entity);//强制执行持久化
void deleteInBatch(Iterable<T> entities);//删除一个实体集合
四、使用JPA进行CRUD和分页
1.在CategoryController中添加映射:增、删、改、查。
1 @RequestMapping("/listCategories") 2 public String ListCategories(Model model, 3 @RequestParam(value = "start",defaultValue = "0")int start, 4 @RequestParam(value = "size",defaultValue = "5") int size 5 ) throws Exception{ 6 start=start<0?0:start; 7 Sort sort=new Sort(Sort.Direction.DESC,"id"); 8 Pageable pageable=new PageRequest(start,size,sort); 9 Page<Category> page=categoryDAO.findAll(pageable); 10 model.addAttribute("page",page); 11 return "listCategories"; 12 } 13 14 //JPA 新增和修改用的都是save. 它根据实体类的id是否为0来判断是进行增加还是修改 15 @RequestMapping("/addCategory") 16 public String addCategory(Category category) throws Exception{ 17 categoryDAO.save(category); 18 return "redirect:listCategories"; 19 } 20 @RequestMapping("/deleteCategory") 21 public String deleteCategory(Category category)throws Exception{ 22 categoryDAO.delete(category); 23 return "redirect:listCategories"; 24 } 25 @RequestMapping("/updateCategory") 26 public String updateCategory(Category category)throws Exception{ 27 categoryDAO.save(category); 28 return "redirect:listCategories"; 29 } 30 @RequestMapping("/editCategory") 31 public String editCategory(int id,Model model)throws Exception{ 32 Category category=categoryDAO.getOne(id); 33 model.addAttribute("category",category); 34 return "editCategory"; 35 }
知识点:
<1>Page是Spring Data提供的一个接口,该接口表示一部分数据的集合以及其相关的下一部分数据、数据总数等相关信息,通过该接口,我们可以得到数据的总体信息(数据总数、总页数...)以及当前数据的信息(当前数据的集合、当前页数等)。
1 public interface Page<T> extends Iterable<T> { 2 3 int getNumber(); //当前第几页 返回当前页的数目。总是非负的 4 5 int getSize(); //返回当前页面的大小。 6 7 int getTotalPages(); //返回分页总数。 8 9 int getNumberOfElements(); //返回当前页上的元素数。 10 11 long getTotalElements(); //返回元素总数。 12 13 boolean hasPreviousPage(); //返回如果有上一页。 14 15 boolean isFirstPage(); //返回当前页是否为第一页。 16 17 boolean hasNextPage(); //返回如果有下一页。 18 19 boolean isLastPage(); //返回当前页是否为最后一页。 20 21 Iterator<T> iterator(); 22 23 List<T> getContent(); //将所有数据返回为List 24 25 boolean hasContent(); //返回数据是否有内容。 26 27 Sort getSort(); //返回页的排序参数。 28 }
<2>Pageable 是Spring Data库中定义的一个接口,该接口是所有分页相关信息的一个抽象,通过该接口,我们可以得到和分页相关所有信息(例如pageNumber、pageSize等),这样,Jpa就能够通过pageable参数来得到一个带分页信息的Sql语句。
1 /** 2 * 分页信息抽象接口 3 * 4 * @author Oliver Gierke 5 */ 6 public interface Pageable { 7 8 /** 9 * 返回要返回的页面. 10 * 11 * @return the page to be returned. 12 */ 13 int getPageNumber(); 14 15 /** 16 * 返回要返回的项目的数量。 17 * 18 * @return the number of items of that page 19 */ 20 int getPageSize(); 21 22 /** 23 * 根据底层页面和页面大小返回偏移量。 24 * 25 * @return the offset to be taken 26 */ 27 int getOffset(); 28 29 /** 30 * 返回排序参数。 31 * 32 * @return 33 */ 34 Sort getSort(); 35 }
2.数据展示页listCategories.html
1 <!DOCTYPE html> 2 <html lang="en" xmlns:th="http://www.thymeleaf.org"> 3 <head> 4 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 5 <title>Title</title> 6 </head> 7 <body> 8 <div class="showing"> 9 <h2>springboot+jpa</h2> 10 <div style="width:500px;margin:20px auto;text-align: center"> 11 <table align="center" border="1" cellspacing="0"> 12 <thead> 13 <tr> 14 <th>id</th> 15 <th>name</th> 16 <td>编辑</td> 17 <td>删除</td> 18 </tr> 19 </thead> 20 <tbody> 21 <tr th:each="c: ${page}"> 22 <td align="center" th:text="${c.id}"></td> 23 <td align="center" th:text="${c.name}"></td> 24 <td align="center" ><a th:href="@{/editCategory(id=${c.id})}">编辑</a></td> 25 <td align="center" ><a th:href="@{/deleteCategory(id=${c.id})}">删除</a></td> 26 </tr> 27 </tbody> 28 </table> 29 <br /> 30 <div> 31 <a th:href="@{/listCategories(start=0)}">[首 页]</a> 32 <a th:href="@{/listCategories(start=${page.number -1})}">[上一页]</a> 33 <a th:href="@{/listCategories(start=${page.number +1})}">[下一页]</a> 34 <a th:href="@{/listCategories(start=${page.totalPages -1})}">[末 页]</a> 35 </div> 36 <form action="/addCategory" method="post"> 37 name:<input name="name"/><br/> 38 <button type="submit">提交</button> 39 </form> 40 </div> 41 </div> 42 </body> 43 </html>
3.修改页面editCategory.html
1 <!DOCTYPE html> 2 <html lang="en" xmlns:th="http://www.thymeleaf.org"> 3 <head> 4 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 5 <title>Title</title> 6 </head> 7 <body> 8 <div class="showing"> 9 <h2>springboot+jpa</h2> 10 11 <div style="margin:0px auto; width:500px"> 12 13 <form action="/updateCategory" method="post"> 14 15 name: <input name="name" th:value="${category.name}" /> <br/> 16 17 <input name="id" type="hidden" th:value="${category.id}" /> 18 <button type="submit">提交</button> 19 20 </form> 21 </div> 22 </div> 23 </body> 24 </html>
4.测试
五、代码