JPA是Java Persistence API的简称,中文名Java持久层API,是JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。JPA并不是ORM框架,它只是一个规范,制定了一套ORM的标准。
点击开源中国下载源码
由于代码内容比较多多,建议优先下载代码,对着代码读更加快捷
SpringBoot学习之旅(七)—JPA进阶篇之自定义查询、修改、分页
SpringBoot学习之旅(八)—JPA进阶篇之联表操作
org.springframework.boot
spring-boot-starter-data-jpa
org.springframework.boot
spring-boot-starter-web
mysql
mysql-connector-java
runtime
org.projectlombok
lombok
1.18.6
com.alibaba
druid-spring-boot-starter
1.1.10
com.alibaba
fastjson
1.2.47
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;
#当前服务的端口
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
生成数据库关联实体
@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;
}
import com.lupf.springbootjpa.dbobject.UserInfo;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface UserRepository extends JpaRepository {
}
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;
}
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 getAll();
//根据id查询
UserInfoModel getUserById(Integer id);
//分页查询
Page getPageInfo(Pageable pageable);
//排序
List 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 getAll() {
List userInfos = userRepository.findAll();
if (null != userInfos && userInfos.size() > 0) {
List userInfoModels = userInfos.stream().map(userInfo -> dbo2model(userInfo)).collect(Collectors.toList());
return userInfoModels;
}
return null;
}
@Override
public UserInfoModel getUserById(Integer id) {
Optional userInfoOptional = userRepository.findById(id);
if (null != userInfoOptional && userInfoOptional.isPresent()) {
return dbo2model(userInfoOptional.get());
}
return null;
}
@Override
public Page getPageInfo(Pageable pageable) {
Page userInfoPage = userRepository.findAll(pageable);
if (null != userInfoPage) {
return pagedbo2model(userInfoPage);
}
return null;
}
@Override
public List getUserInfoBySort(Sort sort) {
List userInfos = userRepository.findAll(sort);
if (userInfos.size() > 0) {
List 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 pagedbo2model(Page userInfoPage) {
if (null == userInfoPage)
return null;
long t = userInfoPage.getTotalElements();
List userInfos = userInfoPage.getContent();
List userInfoModels = userInfos.stream().map(userInfo -> dbo2model(userInfo)).collect(Collectors.toList());
PageImpl 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("[email protected]");
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 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 userInfoModelPage = userService.getPageInfo(pageRequest);
log.info(JSON.toJSONString(userInfoModelPage));
}
//降序查询
@Test
public void getUserInfoBySort() {
Sort sort = new Sort(Sort.Direction.ASC, "age");
List userInfoModels = userService.getUserInfoBySort(sort);
log.info(JSON.toJSONString(userInfoModels));
}
}
略!
本篇的核心是Jpa操作数据库,因此这里就不啰嗦了…