文章目录
什么是JPA
JPA是Java Persistence API的简称,中文名Java持久层API,是JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。JPA并不是ORM框架,它只是一个规范,制定了一套ORM的标准。
源码下载
由于代码内容比较多多,建议优先下载代码,对着代码读更加快捷
其他文章
SpringBoot学习之旅(七)—JPA进阶篇之自定义查询、修改、分页
SpringBoot学习之旅(八)—JPA进阶篇之联表操作
基础工作
基础项目创建
- 创建
File–>new–>project–>Spring Initializr–>next
- 默认配置
查看pom.xml,会看见以下默认加入的配置<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency>
- 添加其他相关引入
在pom.xml下添加一下配置文件<!--用于对象自动生成get set方法的插件--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.6</version> </dependency> <!--使用阿里巴巴的druid的mysql连接池--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.10</version> </dependency> <!--json工具类--> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.47</version> </dependency>
- 创建测试库
创建脚本DROP TABLE IF EXISTS `user_info`; CREATE TABLE `user_info` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(50) NOT NULL DEFAULT '' COMMENT '用户名(昵称)', `sex` tinyint(4) NOT NULL DEFAULT '2' COMMENT '性别 0:女 1:男 2:未知', `age` int(11) NOT NULL DEFAULT '0', `telphone` varchar(15) DEFAULT NULL COMMENT '手机号码(唯一键),null数据是不受唯一键约束的,防止用户是使用三方登录的', `email` varchar(20) NOT NULL DEFAULT '' COMMENT '用户的邮箱', `register_mode` varchar(20) NOT NULL DEFAULT '' COMMENT '注册方式:手机号、微信注册、支付宝', `third_party_id` varchar(64) NOT NULL DEFAULT '' COMMENT '第三方的id', `avatar` varchar(50) NOT NULL DEFAULT '' COMMENT '用户头像', PRIMARY KEY (`id`), UNIQUE KEY `电话号码唯一` (`telphone`) USING HASH ) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8;
- 配置application.yml
#当前服务的端口 server: port: 9004 spring: application: #当前服务的名称 name: spring-boot-jpa #数据库连接相关配置 datasource: url: jdbc:mysql://192.168.1.208:3307/jpa_db?useSSL=false&serverTimezone=UTC username: root password: 123456 #阿里巴巴的druid的mysql连接池 type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.jdbc.Driver
使用IDEA工具生成数据库实体对象
-
配置Mysql的连接
-
数据库测试
配置完之后,可以直接通过IDEA对数据库进行基础的操作
-
添加自动生成工具
file–>project Structure
-
生成数据库关联实体
-
创建数据库实体文件夹
-
生成映射对象
-
JAP常用注解说明
-
@Entity
标识当前对象为一个实体类,映射到数据库的一张表,
该注解使用在类上 -
@Table
如:@Table(name = “user_info”)
用于指明当前类映射到数据库的表名user_info;如对象为:UserInfo,表明为:user_info,该注解可以不用加,默认可以识别
该注解使用在类上
属性列表属性名 属性说明 默认值 示例 name 当前类映射到数据库的表名 “” @Table(name = “user_info”) catalog 所属的数据库目录 “” schema 库名 “” @Table(name = “user_info”, schema = “jpa_db”) uniqueConstraints 唯一键或者联合唯一建 [] @Table(name = “user_info”, schema = “jpa_db”, catalog = “”,uniqueConstraints = {@UniqueConstraint(columnNames={“a”,“b”}),@UniqueConstraint(columnNames={“c”,“d”})}) Index 索引列表 [] @Table(name = “user_info”, indexes = {@Index(columnList = “telphone”)}) -
@Id
用于表明对应的字段为主键ID -
@GeneratedValue
逐渐生成策略,默认自动增长:@GeneratedValue(strategy=GenerationType.AUTO)策略 说明 GenerationType.AUTO JPA自动选择合适的策略,是默认选项 GenerationType.IDENTITY 采用数据库 ID自增长的方式来自增主键字段,Oracle 不支持这种方式 GenerationType.SEQUENCE 通过序列产生主键,通过 @SequenceGenerator 注解指定序列名,MySql 不支持这种方式 GenerationType.TABLE 通过表产生主键,框架借由表模拟序列产生主键,使用该策略可以使应用更易于数据库移植。 -
@Basic
表示一个简单的属性到数据库表的字段的映射
fetch: 表示该属性的读取策略,有 EAGER 和 LAZY 两种,分别表示主支抓取和延迟加载,默认为 EAGER.
optional:表示该属性是否允许为null, 默认为true -
@Column
用于指明当前字段与数据库列的映射关系如:@Column(name = “name”) 表示当前字段映射到数据库的name列
属性说明:属性名 含义 默认值 name 当前字段与数据库列的映射关系 “” unique 是否唯一 false nullable 是否允许为null true insertable 在使用“INSERT”脚本插入数据时,是否需要插入该字段的值 true updatable 在使用“UPDATE”脚本插入数据时,是否需要更新该字段的值。insertable和updatable属性一般多用于只读的属性,例如主键和外键等。这些字段的值通常是自动生成的 true columnDefinition 创建表时,该字段创建的SQL语句,一般用于通过Entity生成表定义时使用 “” table 当映射多个表时,指定表的表中的字段 “” length 字段的长度,当字段的类型为varchar时,该属性才有效 255 precision 表示精度,当字段类型为double时,precision表示数值的总长度 0 scale 表示精度,当字段类型为double时,scale表示小数点所占的位数 0 -
@Transient
字段什么注解都没有的时候默认会有@Basic注解,如果一个字段不需要和数据库的列做关联,则添加@Transient注解 -
@Temporal
时间精度
数据库中对时间的精度分别有:DATE, TIME, 和 TIMESTAMP 三种精度(即单纯的日期,时间,或者两者 兼备);而java中并没有涉及到时间的进度问题,因此可以通过该注解进行标明
TemporalType的类型类型 说明 TemporalType.DATE yyyy-HH-dd TemporalType.TIME hh:mm:ss TemporalType.TIMESTAMP yyyy-HH-dd hh:mm:ss -
使用lombok,简化对象
lombok可以自动生成get set方法;同时我们使用规范的驼峰命名及数据库名创建,对象及列的映射根据默认规则关联即可,因此使用起默认的注解即可;让数据库对象能够更加清晰简洁package com.lupf.springbootjpa.dbobject; import lombok.Data; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; //lombok数据对象注解 @Data @Entity public class UserInfo { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; private String name; private Byte sex; private Integer age; private String telphone; private String email; private String registerMode; private String thirdPartyId; private String avatar; }
Repository创建
- 创建UserRepository继承JpaRepository
JpaRepository两个泛型值分别问数据库关联对象及主键类型import com.lupf.springbootjpa.dbobject.UserInfo; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @Repository public interface UserRepository extends JpaRepository<UserInfo,Integer> { }
- 查看UserRepository 继承关系图
这样使得UserRepository 能使用到父类的各种基础的数据库操作方法 - 默认方法的列表
以下为Repository默认的一些方法,根据方法名和字面意思,基本就可以判断他的功效
Service相关定义
领域模型创建
- 创建管理数据模型的领域模型
package com.lupf.springbootjpa.service.model; import lombok.Data; /** * 用户的领域模型对象 */ @Data public class UserInfoModel { private Integer id; private String name; private Byte sex; private Integer age; private String telphone; private String email; private String avatar; }
定义UserService
- 定义用户的基础操作
package com.lupf.springbootjpa.service; import com.lupf.springbootjpa.service.model.UserInfoModel; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import java.util.List; /** * 用户数据操作的接口 */ public interface UserService { //添加 UserInfoModel save(UserInfoModel userInfoModel); //查询所有 List<UserInfoModel> getAll(); //根据id查询 UserInfoModel getUserById(Integer id); //分页查询 Page<UserInfoModel> getPageInfo(Pageable pageable); //排序 List<UserInfoModel> getUserInfoBySort(Sort sort); }
接口实现
- 基础操作的实现
package com.lupf.springbootjpa.service.impl; import com.lupf.springbootjpa.dbobject.UserInfo; import com.lupf.springbootjpa.repository.UserRepository; import com.lupf.springbootjpa.service.UserService; import com.lupf.springbootjpa.service.model.UserInfoModel; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; @Service public class UserServiceImpl implements UserService { @Autowired UserRepository userRepository; @Override public UserInfoModel save(UserInfoModel userInfoModel) { UserInfo userInfo = model2dbo(userInfoModel); if (null != userInfo) { userInfo.setRegisterMode("byManager"); userInfo.setThirdPartyId("123456"); UserInfo saveUserInfo = userRepository.save(userInfo); return dbo2model(saveUserInfo); } return null; } @Override public List<UserInfoModel> getAll() { List<UserInfo> userInfos = userRepository.findAll(); if (null != userInfos && userInfos.size() > 0) { List<UserInfoModel> userInfoModels = userInfos.stream().map(userInfo -> dbo2model(userInfo)).collect(Collectors.toList()); return userInfoModels; } return null; } @Override public UserInfoModel getUserById(Integer id) { Optional<UserInfo> userInfoOptional = userRepository.findById(id); if (null != userInfoOptional && userInfoOptional.isPresent()) { return dbo2model(userInfoOptional.get()); } return null; } @Override public Page<UserInfoModel> getPageInfo(Pageable pageable) { Page<UserInfo> userInfoPage = userRepository.findAll(pageable); if (null != userInfoPage) { return pagedbo2model(userInfoPage); } return null; } @Override public List<UserInfoModel> getUserInfoBySort(Sort sort) { List<UserInfo> userInfos = userRepository.findAll(sort); if (userInfos.size() > 0) { List<UserInfoModel> userInfoModels = userInfos.stream().map(userInfo -> dbo2model(userInfo)).collect(Collectors.toList()); return userInfoModels; } return null; } //将领域模型对象转换为数据库模型对象 private UserInfo model2dbo(UserInfoModel userInfoModel) { if (null == userInfoModel) { return null; } UserInfo userInfo = new UserInfo(); BeanUtils.copyProperties(userInfoModel, userInfo); return userInfo; } //将数据库对象转换为领域模型对象 private UserInfoModel dbo2model(UserInfo userInfo) { if (null == userInfo) { return null; } UserInfoModel userInfoModel = new UserInfoModel(); BeanUtils.copyProperties(userInfo, userInfoModel); return userInfoModel; } //将从数据库中获取page对象 转换成领域page对象 private PageImpl<UserInfoModel> pagedbo2model(Page<UserInfo> userInfoPage) { if (null == userInfoPage) return null; long t = userInfoPage.getTotalElements(); List<UserInfo> userInfos = userInfoPage.getContent(); List<UserInfoModel> userInfoModels = userInfos.stream().map(userInfo -> dbo2model(userInfo)).collect(Collectors.toList()); PageImpl<UserInfoModel> userInfoModelPage = new PageImpl<>(userInfoModels, userInfoPage.getPageable(), t); return userInfoModelPage; } }
编写测试用例
- 快速创建方式
- 编写测试用例
package com.lupf.springbootjpa.service.impl; import com.alibaba.fastjson.JSON; import com.lupf.springbootjpa.service.UserService; import com.lupf.springbootjpa.service.model.UserInfoModel; import lombok.extern.slf4j.Slf4j; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Sort; import org.springframework.test.context.junit4.SpringRunner; import java.util.List; @RunWith(SpringRunner.class) @SpringBootTest @Slf4j public class UserServiceImplTest { @Autowired UserService userService; //测试添加 @Test public void save() { for (int i = 0; i < 20; i++) { UserInfoModel userInfoModel = new UserInfoModel(); userInfoModel.setAge(i); userInfoModel.setAvatar("https://aaa.com/a.png"); userInfoModel.setEmail("a@qq.com"); userInfoModel.setName("测试添加" + i); userInfoModel.setSex(new Byte("1")); userInfoModel.setTelphone("13612345678" + i); UserInfoModel dbUserInfoModel = userService.save(userInfoModel); log.info(JSON.toJSONString(dbUserInfoModel)); } } //查询所有 @Test public void getAll() { List<UserInfoModel> userInfoModels = userService.getAll(); log.info(JSON.toJSONString(userInfoModels)); } //根据ID查询 @Test public void getUserById() { UserInfoModel userInfoModel = userService.getUserById(19); log.info(JSON.toJSONString(userInfoModel)); } //分页查询 @Test public void getPageInfo() { //这里的page是页码-1 比如第一页,就是1-1=0 第二页就是2-1=1 //size是表示单页显示的行数 PageRequest pageRequest = new PageRequest(0, 3); Page<UserInfoModel> userInfoModelPage = userService.getPageInfo(pageRequest); log.info(JSON.toJSONString(userInfoModelPage)); } //降序查询 @Test public void getUserInfoBySort() { Sort sort = new Sort(Sort.Direction.ASC, "age"); List<UserInfoModel> userInfoModels = userService.getUserInfoBySort(sort); log.info(JSON.toJSONString(userInfoModels)); } }
基础操作测试
-
测试添加
-
测试查询所有
-
根据ID查询
-
分页查询
-
升降序查询
Controller定义
略!
本篇的核心是Jpa操作数据库,因此这里就不啰嗦了…