JPA的简单使用

简介

JPA是Java Persistence API的简称,中文名Java持久层API,是JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体[对象持久化](https://baike.baidu.com/item/对象持久化/7316192)到数据库中。 ([来源:百度百科](https://baike.baidu.com/item/JPA/5660672))。总结一点就是对于简单的CRUD,只需要操作对应的实体类,无需编写SQL即可实现对应的功能,同时也支持自定义SQL,详情可查看[JPA官方操作手册](https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#)。

版本介绍

版本备注
SpringBoot2.6.4
MySQL5.6.51
p6spy3.9.1用于格式化sql文件,可以将占位符直接替换成参数值,将执行sql直接打印出来

准备工作

测试表相关SQL

用户表

DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (
  `id` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '主键id',
  `account` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '账户名',
  `password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '密码',
  `user_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '名称',
  `gender` int(1) NULL DEFAULT NULL COMMENT '性别',
  `age` int(3) NULL DEFAULT NULL COMMENT '年龄',
  `create_user` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '创建人',
  `create_time` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6) COMMENT '创建时间',
  `update_user` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '更新人',
  `update_time` timestamp(6) NOT NULL DEFAULT '0000-00-00 00:00:00.000000' COMMENT '更新时间',
  `delete_flag` varchar(1) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '删除标识',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '用户表' ROW_FORMAT = Compact;

用户机构表

DROP TABLE IF EXISTS `user_org`;
CREATE TABLE `user_org`  (
  `id` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '主键id',
  `user_id` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '用户id',
  `org_code` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '机构代码',
  `org_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '机构名称',
  `create_user` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '创建人',
  `create_time` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6) COMMENT '创建时间',
  `update_user` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '更新人',
  `update_time` timestamp(6) NOT NULL DEFAULT '0000-00-00 00:00:00.000000' COMMENT '更新时间',
  `delete_flag` varchar(1) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '删除标识',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '用户表机构表' ROW_FORMAT = Compact;

引入依赖

<dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.7.4</version>
        </dependency>
        <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>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!-- https://mvnrepository.com/artifact/p6spy/p6spy -->
        <dependency>
            <groupId>p6spy</groupId>
            <artifactId>p6spy</artifactId>
            <version>3.9.1</version>
        </dependency>

编写配置文件

application.properties

spring.datasource.url=jdbc:p6spy:mysql://localhost:3306/demo-jpa
spring.datasource.driver-class-name=com.p6spy.engine.spy.P6SpyDriver
spring.datasource.username=root
spring.datasource.password=root

spy.properties

logMessageFormat=com.p6spy.engine.spy.appender.CustomLineFormat
customLogMessageFormat=sql -> %(sqlSingleLine)
appender=com.p6spy.engine.spy.appender.StdoutLogger

通用审计日志基础信息配置

由于数据创建人、创建时间以及数据修改人、修改时间为公共字段,实现AuditorAware接口配合@CreateBy、@CreateDate、@LastModifiedBy、@LastModifiedDate即可字段插入对应的字段信息。

@Component
@EnableJpaAuditing
public class JpaAuditConfig implements AuditorAware<String> {
    /**
     * 可 用于 获取 当前操作人信息,根据实际项目做调整
     * @return
     */
    @Override
    public Optional<String> getCurrentAuditor() {
        // 当前测试为固定在,项目中根据实际情况调整即可
        String operatorId = "operateId";
        return Optional.of(operatorId);
    }

自定义主键生成策略

由于多表新增时涉及到关联主键的问题,故表的主键需要先生成备用,新增数据时的主键生成逻辑需要调整:若新增的实体类已存在主键,则不重新生成。

jpa主键生成策略可参考:https://www.cnblogs.com/SummerinShire/p/7544897.html

public class CustomUUIDConfig extends UUIDGenerator {
    /**
     *
     * @param session
     * @param entityObj 当前实体类对象
     * @return
     * @throws HibernateException
     */
    @Override
    public Serializable generate(SharedSessionContractImplementor session, Object entityObj) throws HibernateException {
        String entityIdField = this.getEntityIdField(entityObj);
        if (StrUtil.isBlank(entityIdField)) {
            throw new RuntimeException(" Entity must contains id annotation");
        }
        Object existId = ReflectUtil.getFieldValue(entityObj, entityIdField);
        if (ObjectUtil.isNotNull(existId)) {
            return (Serializable) existId;
        }
        return IdUtil.fastSimpleUUID();
    }

    /**
     * 获得实体类中id对应的字段名称
     * @param entityObj
     * @return
     */
    private String getEntityIdField(Object entityObj) {
        String idFieldName = null;
        Field[] fields = ReflectUtil.getFields(entityObj.getClass());
        for (Field field : fields) {
            if (AnnotationUtil.hasAnnotation(field, Id.class)) {
                idFieldName = field.getName();
                break;
            }
        }
        return idFieldName;
    }
}

jpa 主键生成的4中策略

编写实体类

用户实体类

@Data
@Entity
@ToString
@Table(name = "user")
@Accessors(chain = true)
@EntityListeners(AuditingEntityListener.class)
public class UserPO implements Serializable {

    private static final long serialVersionUID = -228275281168696921L;

    /**
     * 主键id,使用自定义的主键生成策略
     */
    @Id
    @Column
    @GeneratedValue(generator = "my-uuid")
    @GenericGenerator(name = "my-uuid", strategy = "top.zhoujb.demo.jpa.config.CustomUUIDConfig")
    private String id;
    /**
     * 账户名
     */
    @Column
    private String account;
    /**
     * 密码
     */
    @Column
    private String password;
    /**
     * 名称
     */
    @Column
    private String userName;
    /**
     * 性别
     */
    @Column
    private Integer gender;
    /**
     * 年龄
     */
    @Column
    private Integer age;
    /**
     * 创建人
     */
    @Column
    @CreatedBy
    private String createUser;
    /**
     * 创建时间
     */
    @Column
    @CreatedDate
    private LocalDateTime createTime;
    /**
     * 更新人
     */
    @Column
    @LastModifiedBy
    private String updateUser;
    /**
     * 更新时间
     */
    @Column
    @LastModifiedDate
    private LocalDateTime updateTime;
    /**
     * 删除标识
     */
    @Column
    private String deleteFlag;

    /**
     * 机构代码
     */
    @Transient
    private String orgCode;
    /**
     * 机构名称
     */
    @Transient
    private String orgName;
}

用户机构实体类

@Data
@Entity
@ToString
@Accessors(chain = true)
@Table(name = "user_org")
@EntityListeners(AuditingEntityListener.class)
public class UserOrgPO implements Serializable {

    private static final long serialVersionUID = 8618860155842190730L;
    /**
     * 主键id,使用自定义的主键生成策略
     */
    @Id
    @Column
    @GeneratedValue(generator = "my-uuid")
    @GenericGenerator(name = "my-uuid", strategy = "top.zhoujb.demo.jpa.config.CustomUUIDConfig")
    private String id;
    /**
     * 用户id
     */
    private String userId;
    /**
     * 机构代码
     */
    private String orgCode;
    /**
     * 机构名称
     */
    private String orgName;
    /**
     * 创建人
     */
    private String createUser;
    /**
     * 创建时间
     */
    private LocalDateTime createTime;
    /**
     * 更新人
     */
    private String updateUser;
    /**
     * 更新时间
     */
    private LocalDateTime updateTime;
    /**
     * 删除标识
     */
    private String deleteFlag;
}

数据库操作接口

用户数据库访问接口

public interface UserDao extends JpaRepository<UserPO, String> {}

用户机构数据库访问层接口

public interface UserOrgDao extends JpaRepository<UserOrgPO, String> {}

测试案例

新增

单表新增

    @Test
    void insertOne() {
        String password = SecureUtil.md5("user");
        UserPO userPO = new UserPO()
                .setAccount("user")
                .setPassword(password)
                .setUserName("测试用户")
                .setAge(20)
                .setGender(1)
                .setDeleteFlag("0");
        this.userDao.save(userPO);
    }

多表新增

    @Test
    void insertMultipleWithCustomId() {
        String password = SecureUtil.md5("user");
        List<UserPO> userList = new ArrayList<>();
        List<UserOrgPO> userOrgList = new ArrayList<>();
        for (int i = 1; i <= 10; i++) {
            String userId = IdUtil.fastSimpleUUID();
            UserPO userPO = new UserPO()
                    .setId(userId)
                    .setAccount("user" + i + RandomUtil.randomInt())
                    .setPassword(password)
                    .setUserName("测试用户" + i + RandomUtil.randomInt())
                    .setAge(20 + i)
                    .setGender(i % 2)
                    .setDeleteFlag("0");
            userList.add(userPO);
            UserOrgPO userOrgPO = new UserOrgPO()
                    .setUserId(userId)
                    .setDeleteFlag("0")
                    .setOrgCode("orgCode" + i + RandomUtil.randomInt())
                    .setOrgName("orgName" + i + RandomUtil.randomInt());
            userOrgList.add(userOrgPO);
        }
        this.userDao.saveAllAndFlush(userList);
        this.userOrgDao.saveAllAndFlush(userOrgList);
    }

修改

    @Test
    public void update() {
        String userId = "5d744def266146fc9b99d7f7b87883e4";
        Optional<UserPO> opt = this.userDao.findById(userId);
        UserPO userPO = new UserPO();
        if (opt.isPresent()) {
            BeanUtil.copyProperties(opt.get(), userPO);
        }
        userPO.setUserName("777").setAccount("user666");
        UserPO save = this.userDao.save(userPO);
        System.out.println(save.toString());
    }

删除

    @Test
    public void deleteOne() {
        String userId = "2e78a777172b46e392cc2416ad0043b4";
        this.userDao.deleteById(userId);
    }

    @Test
    public void deleteMany() {
        List<String> idList = ListUtil.toList("f4394921d44243228fe15115fa4ea693", "ce99a184246a4af5ae5f3bbabf0dd807");
        this.userDao.deleteAllByIdInBatch(idList);
    }

查询

简单查询

    @Test
    public void findOne() {
        String userId = "a1f704b9600548e6a71e6d694e04698a";
        Optional<UserPO> byId = this.userDao.findById(userId);
        if (byId.isPresent()) {
            System.out.println(byId.get().toString());
        } else {
            System.out.println("no data");
        }
    }

    @Test
    public void findAll() {
        List<UserPO> all = this.userDao.findAll();
        System.out.println(all.size());
    }

普通单表分页

自定义分页方法,参数为实体类时,在UserDao接口中添加如下代码

    /**
     * 分页查询数据
     * @param params
     * @param pageRequest
     * @return
     */
    @Query(nativeQuery=true, value = "select * from user where account like %:#{#user.account}%")
    Page<UserPO> selectByPage(@Param("user") UserPO params, Pageable pageRequest);

测试代码

    @Test
    public void findByPage() {
        int page = 1;
        int size = 5;
        Sort genderSort = Sort.by(Sort.Direction.ASC, "gender");
        Pageable pageRequest = PageRequest.of(page, size, genderSort);
        UserPO params = new UserPO().setAccount("user");
        Page<UserPO> poPage = this.userDao.selectByPage(params, pageRequest);
        System.out.println(poPage.getContent().size());
    }

多表联合分页

多表分页,参数为实体类时,在UserDao接口中添加如下代码。注意:此时需要自己重写countQuery,否则会导致sql执行报错或者统计结果错误等问题。

    /**
     * 分页查询数据
     * @param params
     * @param pageRequest
     * @return
     */
    @Query(nativeQuery = true,
            value = "select u.*,uo.org_code,uo.org_name from user u join user_org uo on uo.user_id = u.id where u.account like %:#{#user.account}%"
            , countQuery = "select count(*) from user u join user_org uo on uo.user_id = u.id where u.account like %:#{#user.account}%"
    )
    Page<UserPO> selectUseAndOrgByPage(@Param("user") UserPO params, Pageable pageRequest);

测试代码

    @Test
    public void findByPageWithMultipleTable() {
        int page = 1;
        int size = 5;
        Sort genderSort = Sort.by(Sort.Direction.ASC, "gender");
        Pageable pageRequest = PageRequest.of(page, size, genderSort);
        UserPO params = new UserPO().setAccount("user");
        Page<UserPO> poPage = this.userDao.selectUseAndOrgByPage(params, pageRequest);
        List<UserPO> content = poPage.getContent();
        if (CollUtil.isNotEmpty(content)) {
            content.stream().forEach(uo -> System.out.println(uo.toString()));
        }
    }

总结

注意事项

说明
查询参数为实体类时参数书写方式格式: :#{#实体类参数名.实体类属性名}。示例:实体类参数名称为:params,其中有name属性,则在sql中应为::#{#params.name}
多表联合查询注意事项多表联合分页查询时需要统计sql

jpa 的关键字使用

关键字案例执行的SQL片段
DistinctfindDistinctByLastnameAndFirstnameselect distinct … where x.lastname = ?1 and x.firstname = ?2
AndfindByLastnameAndFirstname… where x.lastname = ?1 and x.firstname = ?2
OrfindByLastnameOrFirstname… where x.lastname = ?1 or x.firstname = ?2
Is, EqualsfindByFirstname,findByFirstnameIs,findByFirstnameEquals… where x.firstname = ?1
BetweenfindByStartDateBetween… where x.startDate between ?1 and ?2
LessThanfindByAgeLessThan… where x.age < ?1
LessThanEqualfindByAgeLessThanEqual… where x.age <= ?1
GreaterThanfindByAgeGreaterThan… where x.age > ?1
GreaterThanEqualfindByAgeGreaterThanEqual… where x.age >= ?1
AfterfindByStartDateAfter… where x.startDate > ?1
BeforefindByStartDateBefore… where x.startDate < ?1
IsNull, NullfindByAge(Is)Null… where x.age is null
IsNotNull, NotNullfindByAge(Is)NotNull… where x.age not null
LikefindByFirstnameLike… where x.firstname like ?1
NotLikefindByFirstnameNotLike… where x.firstname not like ?1
StartingWithfindByFirstnameStartingWith… where x.firstname like ?1 (parameter bound with appended %)
EndingWithfindByFirstnameEndingWith… where x.firstname like ?1 (parameter bound with prepended %)
ContainingfindByFirstnameContaining… where x.firstname like ?1 (parameter bound wrapped in %)
OrderByfindByAgeOrderByLastnameDesc… where x.age = ?1 order by x.lastname desc
NotfindByLastnameNot… where x.lastname <> ?1
InfindByAgeIn(Collection ages)… where x.age in ?1
NotInfindByAgeNotIn(Collection ages)… where x.age not in ?1
TRUEfindByActiveTrue()… where x.active = true
FALSEfindByActiveFalse()… where x.active = false
IgnoreCasefindByFirstnameIgnoreCase… where UPPER(x.firstname) = UPPER(?1)

来源:https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods.query-creation

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇