Java学习笔记-Day84 Spring Boot框架(四)
一、JPA的概念
JPA(Java Persistence API)是JDK 5.0注解或XML描述对象与关系表的映射关系,并将运行期的实体对象持久化到数据库中。
二、JPA的优势
(1)标准化:JPA 是 JCP 组织发布的 Java EE 标准之一,因此任何声称符合 JPA 标准的框架都遵循同样的架构,提供相同的访问API,这保证了基于JPA开发的企业应用能够经过少量的修改就能够在不同的JPA框架下运行。
(2)容器级特性的支持:JPA框架中支持大数据集、事务、并发等容器级事务,这使得 JPA 超越了简单持久化框架的局限,在企业应用发挥更大的作用。
(3)简单方便:JPA的主要目标之一就是提供更加简单的编程模型:在JPA框架下创建实体和创建Java 类一样简单,没有任何的约束和限制,只需要使用 javax.persistence.Entity进行注释,JPA的框架和接口也都非常简单,没有太多特别的规则和设计模式的要求,开发者可以很容易的掌握。JPA基于非侵入式原则设计,因此可以很容易的和其它框架或者容器集成。
(4)查询能力:JPA的查询语言是面向对象而非面向数据库的,它以面向对象的自然语法构造查询语句,可以看成是Hibernate HQL的等价物。JPA定义了独特的JPQL(Java Persistence Query Language),JPQL是EJB QL的一种扩展,它是针对实体的一种查询语言,操作对象是实体,而不是关系数据库的表,而且能够支持批量更新和修改、JOIN、GROUP BY、HAVING 等通常只有 SQL 才能够提供的高级查询特性,甚至还能够支持子查询。
(5)高级特性:JPA 中能够支持面向对象的高级特性,如类之间的继承、多态和类之间的复杂关系,这样的支持能够让开发者最大限度的使用面向对象的模型设计企业应用,而不需要自行处理这些特性在关系数据库的持久化。
三、JPA的使用
Spring Data JPA 是Spring Data 的一个子项目,它通过提供基于JPA的Repository极大了减少了操作JPA的代码。Spring Boot使用Spring Data JPA完成CRUD操作,数据的存储以及访问都是最为核心的关键部分。
步骤:
(1)点击 New Project -> 选择Spring Initializr -> 点击Next -> Java Version选择8 -> 点击Next -> 选择Web的Spring Web、SQL的Spring Data JPA和MySQL Driver。
(2)配置application.properties文件。
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/bookdb?serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=root
spring.jpa.database=mysql
spring.jpa.show-sql=true
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
(3)在数据库bookdb中创建一个t_user表。
(4)创建实体类,该实体类实现序列化,并在类前添加@Entity和@Table注解,在实体类中对应数据库表主键的属性上添加@Id、@GeneratedValue、@Column注解,实体类的属性与数据库表的别名不相同的话,则需要在属性前添加@Column注解。
package com.etc.ssm.entity;
import javax.persistence.*;
import java.io.Serializable;
import java.util.Date;
@Entity
@Table(name="t_user")
public class User implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name="userid")
private Integer userid;
private String username;
private String userphone;
private String userpwd;
private Integer userage;
private Integer usersex;
private Date userregistertime;
private Integer ustatus;
private String userpictureurl;
private static final long serialVersionUID = 1L;
public Integer getUserid() {
return userid;
}
public void setUserid(Integer userid) {
this.userid = userid;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getUserphone() {
return userphone;
}
public void setUserphone(String userphone) {
this.userphone = userphone;
}
public String getUserpwd() {
return userpwd;
}
public void setUserpwd(String userpwd) {
this.userpwd = userpwd;
}
public Integer getUserage() {
return userage;
}
public void setUserage(Integer userage) {
this.userage = userage;
}
public Integer getUsersex() {
return usersex;
}
public void setUsersex(Integer usersex) {
this.usersex = usersex;
}
public Date getUserregistertime() {
return userregistertime;
}
public void setUserregistertime(Date userregistertime) {
this.userregistertime = userregistertime;
}
public Integer getUstatus() {
return ustatus;
}
public void setUstatus(Integer ustatus) {
this.ustatus = ustatus;
}
public String getUserpictureurl() {
return userpictureurl;
}
public void setUserpictureurl(String userpictureurl) {
this.userpictureurl = userpictureurl;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(getClass().getSimpleName());
sb.append(" [");
sb.append("Hash = ").append(hashCode());
sb.append(", userid=").append(userid);
sb.append(", username=").append(username);
sb.append(", userphone=").append(userphone);
sb.append(", userpwd=").append(userpwd);
sb.append(", userage=").append(userage);
sb.append(", usersex=").append(usersex);
sb.append(", userregistertime=").append(userregistertime);
sb.append(", ustatus=").append(ustatus);
sb.append(", userpictureurl=").append(userpictureurl);
sb.append(", serialVersionUID=").append(serialVersionUID);
sb.append("]");
return sb.toString();
}
public User(String username, String userphone, String userpwd, Integer userage, Integer usersex,
Date userregistertime, Integer ustatus, String userpictureurl) {
super();
this.username = username;
this.userphone = userphone;
this.userpwd = userpwd;
this.userage = userage;
this.usersex = usersex;
this.userregistertime = userregistertime;
this.ustatus = ustatus;
this.userpictureurl = userpictureurl;
}
public User(String username, String userphone, String userpwd, Integer userage, Integer usersex,
String userpictureurl) {
super();
this.username = username;
this.userphone = userphone;
this.userpwd = userpwd;
this.userage = userage;
this.usersex = usersex;
this.userpictureurl = userpictureurl;
}
public User(Integer userid, String username, String userphone, String userpwd, Integer userage, Integer usersex,
String userpictureurl) {
super();
this.userid = userid;
this.username = username;
this.userphone = userphone;
this.userpwd = userpwd;
this.userage = userage;
this.usersex = usersex;
this.userpictureurl = userpictureurl;
}
public User() {
// TODO Auto-generated constructor stub
}
}
(5)将@Table注解与bookdb数据库关联。点击idea右侧的Database -> 点击 + 号 -> 选择Data Source -> 选择MySQL。
在General选项页输入IP地址、端口号、账号、密码、数据库名。点击Test Connecton,会出现时区错误,点击set time zone设置时区。将servertimezone修改为Asia/Shanghai。
添加成功后,会在右侧显示如下界面。
将光标移动至实体类报错的@Table注解的name属性的值,此时会显示一个窗口,点击Assign Data Sources,选择对应的数据库。
(6)创建Dao接口,该接口继承JpaRepository接口(SpringDataJPA提供的简单数据操作接口)、JpaSpecificationExecutor(SpringDataJPA提供的复杂查询接口)、Serializable(序列化接口)。SpringDataJPA内部使用了类代理的方式让继承了它接口的子接口都以Spring管理的Bean的形式存在,可以直接使用@Autowired注解在Spring管理bean上使用.
package com.etc.ssm.dao;
import com.etc.ssm.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import java.io.Serializable;
public interface UserDao extends JpaRepository<User,Integer>, JpaSpecificationExecutor<User>, Serializable {
}
(7)在src\test\java\com\etc\ssm目录中创建Dao的测试类,并运行@Test注解的方法。
package com.etc.ssm;
import com.etc.ssm.dao.UserDao;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.Date;
@SpringBootTest
class SsmApplicationTests {
@Autowired
UserDao userDao;
@Test
void contextLoads() {
System.out.println(userDao.findById(1));
}
}
四、SpringDataJpa
1、SpringDataJPA简介
SpringData是Spring提供了一个操作数据的框架。Spring Data JPA是SpringData框架下的一个基于JPA标准操作数据的模块,Spring Data JPA基于JPA的标准数据进行操作,简化操作持久层的代码,只需要编写接口。
核心接口:
(1)Repository:最顶层的接口,是一个空的接口,目的是为了统一所有Repository的类型,且能让组件扫描的时候自动识别。
(2)CrudRepository :是Repository的子接口,提供CRUD的功能。
(3)PagingAndSortingRepository:是CrudRepository的子接口,添加分页和排序的功能。
(4)JpaRepository:是PagingAndSortingRepository的子接口,增加了一些实用的功能,比如批量操作等。
(5)JpaSpecificationExecutor:用来做复杂查询的接口。类似条件(QBC)查询。
(6)Specification:是Spring Data JPA提供的一个查询规范,要做复杂的查询,只需围绕这个规范来设置查询条件即可
2、SpringDataJPA的增删改查方法
SpringDataJPA默认情况下,提供了查询的相关的方法,基本上能满足我们80%左右的需要,但是还有一些是没有满足的。我们可以遵循它的命名规范来定义方法名。如果没有满足命名规范的,可以在方法上加@Query注解来写语句。
(1)使用JPA提供的方法
- UserDao.java
package com.etc.ssm.dao;
import com.etc.ssm.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import java.io.Serializable;
public interface UserDao extends JpaRepository<User,Integer>, JpaSpecificationExecutor<User>, Serializable {
}
- SsmApplicationTests.java
package com.etc.ssm;
import com.etc.ssm.dao.UserDao;
import com.etc.ssm.entity.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Sort;
import java.util.Date;
@SpringBootTest
class SsmApplicationTests {
@Autowired
UserDao userDao;
@Test
void contextLoads() {
System.out.println(userDao.findById(1));
}
@Test
void contextLoadsAll() {
System.out.println(userDao.findAll());
}
@Test
void getUserOrder() {
System.out.println(userDao.findAll(Sort.by(Sort.Direction.DESC, "userid")));
}
@Test
void addUser() {
User user = new User();
user.setUsername("tom");
user.setUserphone("12345678912");
user.setUserpwd("e10adc3949ba59abbe56e057f20f883e");
user.setUserage(25);
user.setUserpictureurl("12/1.jpg");
user.setUserregistertime(new Date());
user.setUsersex(1);
user.setUstatus(0);
userDao.save(user);
}
}
(2)使用JPA命名规范语法的方法
- UserDao.java
package com.etc.ssm.dao;
import com.etc.ssm.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import java.io.Serializable;
import java.util.List;
public interface UserDao extends JpaRepository<User,Integer>, JpaSpecificationExecutor<User>, Serializable {
public List<User> findByUsernameContaining(String username);
}
- SsmApplicationTests.java
package com.etc.ssm;
import com.etc.ssm.dao.UserDao;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class SsmApplicationTests {
@Autowired
UserDao userDao;
@Test
void getUserByUsername() {
System.out.println(userDao.findByUsernameContaining("小"));
}
}
(3)使用自定义的方法(nativeQuery = true
代表原生SQL)
- UserDao.java
package com.etc.ssm.dao;
import com.etc.ssm.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Query;
import java.io.Serializable;
public interface UserDao extends JpaRepository<User,Integer>, JpaSpecificationExecutor<User>, Serializable {
@Query(nativeQuery = true,value = "select * from t_user where userid = ?")
public User findByUserId(int userid);
}
- SsmApplicationTests.java
package com.etc.ssm;
import com.etc.ssm.dao.UserDao;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class SsmApplicationTests {
@Autowired
UserDao userDao;
@Test
void getUserByUserId() {
System.out.println(userDao.findByUserId(1));
}
}
五、使用JPQL语法实现模糊查询与分页
JPQL是一种查询语言,具有与SQL相类似的特征,JPQL是完全面向对象的,具备继承、多态和关联等特性,和hibernate HQL很相似。
Pageable是Spring Data库中定义的一个接口,该接口是所有分页相关信息的一个抽象,通过该接口,我们可以得到和分页相关所有信息(例如pageNumber、pageSize等),JPA能够通过pageable参数来得到一个带分页信息的SQL语句。
Page是Spring Data库中定义的一个接口,该接口表示一部分数据的集合以及其相关的下一部分数据、数据总数等相关信息,通过该接口,我们可以得到数据的总体信息(数据总数、总页数等)以及当前数据的信息(当前数据的集合、当前页数等)。
Spring Data Jpa除了会通过命名规范帮助我们扩展Sql语句外,还会帮助我们处理类型为Pageable的参数,将pageable参数转换成为sql语句中的条件,同时,还会帮助我们处理类型为Page的返回值,当发现返回值类型为Page,Spring Data Jpa将会把数据的整体信息、当前数据的信息、分页的信息都放入到返回值中。这样,我们就能够方便的进行个性化的分页查询。
- UserDao.java
package com.etc.ssm.dao;
import com.etc.ssm.entity.User;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import java.io.Serializable;
@Repository
public interface UserDao extends JpaRepository<User,Integer>, JpaSpecificationExecutor<User>, Serializable {
@Query("select u from User u where u.username like %?1%")
Page<User> selectByLike(String keywords, Pageable pageable);
}
- UserServiceImpl.java
package com.etc.ssm.service.impl;
import com.etc.ssm.dao.UserDao;
import com.etc.ssm.entity.User;
import com.etc.ssm.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserServiceImpl implements UserService {
@Autowired
UserDao userDao;
@Override
public Page<User> showUsers(int page, int pageSize, String keywords) {
if (page < 1) {
page = 1;
}
// page为0代表第一页,pageSize代表每页的记录数
Pageable pageable = PageRequest.of(page - 1, pageSize);
Page<User> pd = userDao.selectByLike(keywords, pageable);
return pd;
}
}