JPA学习2 - 核心注解、注解进行增删改查、List查询结果返回类型、一对多、多对一、多对多

x33g5p2x  于2022-06-27 转载在 其他  
字(17.9k)|赞(0)|评价(0)|浏览(247)

SpringDataJPA

概述

Spring提供的一套对JPA操作更加高级的框架,是在JPA规范下专门用来进行持久化的解决方案

核心注解

策略策略策略策略a@MappedSuperclass:标记这个基类存储表的共同字段,继承类直接继承即可@Entity:标记这个实体类是与数据库表映射的@Table:标记这个实体的表信息@Id:标记该属性是主键列@GeneratedValue:标记该主键的生成策略IDENTITY == 自增 == MySQL支持SEQUENCE == 序列 == Oracle支持AUTO == 程序自动帮我们选择策略TABLE == 序列 == JPA提供的一个机制,通过数据库表形式帮助主键自增 = 所有表共用一个自增字段=如下图@Query:自定义SQL语句 = 查询、修改、删除语句value:JPQL、SQL语句nativeQuery:默认false,JPQL语句查询,true本地SQL语句查询@Modify:用于标识 修改、删除的方法@Column:标记该列的信息@OneToMany:一对多的注解,一般标识在list属性上@JoinColumn:一对多,多对一,主表主键与关联表外键的定义@ManyToOne:多对一的注解,一般标识在JavaBean属性上@NotFound:没有找到记录时的行为,默认是抛出异常

简单使用

配置打印的SQL携带参数

pom.xml

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

        <!--hibernate对JPA规范的支持包-->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>5.4.30.Final</version>
        </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>

        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.6.3</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/junit/junit -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>

        <!--监控sql日志 == SQL可以携带参数,如果用JPA自带的SQL打印是没携带参数的-->
        <dependency>
            <groupId>org.bgee.log4jdbc-log4j2</groupId>
            <artifactId>log4jdbc-log4j2-jdbc4.1</artifactId>
            <version>1.16</version>
        </dependency>

    </dependencies>

application.yml == 注意mysql的驱动名以及数据库的连接URL

server:
  port: 8080

spring:
  datasource:
    username: root
    password: root
#    url: jdbc:mysql://localhost:3306/lrc_blog_test2?useSSL=false&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
    url: jdbc:log4jdbc:mysql://localhost:3306/lrc_blog_test2?useSSL=false&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
#    driver-class-name: com.mysql.cj.jdbc.Driver
    driver-class-name: net.sf.log4jdbc.sql.jdbcapi.DriverSpy
  jpa:
    show-sql: true
    database-platform: org.hibernate.dialect.MySQL8Dialect
    database: mysql
    hibernate:
#      ddl-auto: create

原理 = JDK动态代理

  1. @Repository定义的dao接口会经过JDK动态代理转成 SimpleJpaRepository对象执行具体的CRUD操作
  2. SimpleJpaRepository通过EntityManager对象执行具体的CRUD操作
  3. 而EntityManager具体的实现由Hibernate进行执行JDBC操作

简单代码 = 内置接口

application.yml

server:
  port: 8080

spring:
  datasource:
    username: root
    password: root
    url: jdbc:mysql://localhost:3306/lrc_blog_test?useSSL=false&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
    driver-class-name: com.mysql.cj.jdbc.Driver
  jpa:
    show-sql: true
    database-platform: org.hibernate.dialect.MySQLDialect
    database: mysql
    open-in-view: true

Book.java

@Getter
@Setter
@ToString
@Entity
@Table(name = "book")
public class Book implements Serializable {
    private static final long serialVersionUID = 2095940921263481761L;


    /** 主键 - 记录插入自动填充主键处理{@path application.yml} */
    @Id
    @Column(name = "id")
    private String id;

    @Column(name = "create_time")
    private String createTime;

    @Column(name = "update_time")
    private String updateTime;

    /** 记录是否被逻辑删除:0未删除 1逻辑删除 - 逻辑处理值定义{@path application.yml}*/
    @Column(name = "is_del")
    private Integer isDel;

    /** 书籍状态;-1违规  0发布且公开  1发布且私密 - 默认0 */
    @Column(name = "status")
    private String status;

    /** 书名 */
    @Column(name = "name")
    private String name;

    /** 作者 */
    @Column(name = "author")
    private String author;

    /** 作者国籍 */
    @Column(name = "country")
    private String country;

    /** 下载地址 */
    @Column(name = "download_url")
    private String downloadUrl;

    /** 文件类型 */
    @Column(name = "file_type")
    private String fileType;

    /** 阅读感受 */
    @Column(name = "reading_feeling")
    private String readingFeeling;

    /** 书籍分享者 - 昵称 */
    @Column(name = "sharer")
    private String sharer;

    /** 书籍是否违规:0未违规 1违规 - 违规不可显示 */
    @Column(name = "is_violation")
    private Integer isViolation;



    //书籍状态
    public final  static String STATUS_VIOLATION = "-1";
    public final  static String STATUS_PUBLISH_PUBLIC = "0";
    public final  static String STATUS_PUBLISH_PRIVATE = "1";

}

BookDao.java

@Repository
public interface BookDao extends JpaRepository<Book, String> {
}

JPATest.java

@SpringBootTest
public class JPATest {

    @Autowired
    BookDao bookDao;

    @Test
    public void test1() {

        List<Book> books = bookDao.findAll();
        books.stream().forEach(System.out::println);

    }

}

自定义SQL语句

查询
方式1:dao方法名查询形式 - 方法名必须findBy开头

通过方法名,JPA自动识别JPQL语句

SysUserDao.java

@Repository
public interface SysUserDao extends JpaRepository<SysUser, String> {

    public SysUser findByIdAndNickName(String id, String nickName);

}

Test.java

@Test
    public void test8() {
        SysUser sysUser = sysUserDao.findByIdAndNickName("d32478155b6530951cf6b3da56848d5c", "嗯嗯");
        System.out.println(sysUser);
    }

方式2:JPQL查询形式
@Repository
public interface BookDao extends JpaRepository<Book, String>, JpaSpecificationExecutor<Book> {

    //JPA根据方法名进行生成SQL
    public List<Book> findByNameLike(String name);

    //手动写JPQL语句  第一个参数为:?1   第二个参数为:?2  以此类推
    @Query("from Book where name like ?1")
    public List<Book> findByNamexxx(String name);

    //手动写JPQL语句  使用 :参数名 用来填充JPQL的参数
    @Query("from Book where name like :name")
    public List<Book> findByNamexxx2(String name);

}


test.java

@Test
    public void test4() {
        
        
        List<Book> books = bookDao.findByNameLike("%大脑%");
        books.stream().forEach(System.out::println);

        System.out.println("\n=========\n");

        books = bookDao.findByNamexxx("%大脑%");
        books.stream().forEach(System.out::println);

        System.out.println("\n=========\n");

        books = bookDao.findByNamexxx2("%大脑%");
        books.stream().forEach(System.out::println);

    }

方式3:SQL查询形式

BookDao.java

@Repository
public interface BookDao extends JpaRepository<Book, String>, JpaSpecificationExecutor<Book> {

    @Query(value = "select * from book where name like :name ", nativeQuery = true)
    public List<Book> findByNamexxx2(String name);

    @Query(value = "select * from book where name like ?1 ", nativeQuery = true)
    public List<Book> findByNamexxx3(String name);

}

BookDao.java

@Test
    public void test5() {
        List<Book> books = bookDao.findByNamexxx2("%大脑%");
        books.stream().forEach(System.out::println);
    }
    @Test
    public void test6() {
        List<Book> books = bookDao.findByNamexxx3("%大脑%");
        books.stream().forEach(System.out::println);
    }
方式4:代码级别查询,类似MyBatisPlus

dao接口条件继承JpaRepository、JpaSpecificationExecutor这两个接口当需要代码级别控制SQL查询条件,则使用JpaSpecificationExecutor里面的方法查询条件类Specification == toPredicate(Root , CriteriaQuery, CriteriaBuilder)参数1:查询的根对象,查询对象的任务属性都可从此获取参数2:顶层查询对象,用于自定义查询方式(我们很少用)参数3:查询构造器,封装很多的SQL查询条件【like、between等】

关键类Specification:生成where查询条件Sort:记录排序,即order by部分Pageable:分页部分,即limit关键字

BookDao.java

@Repository
public interface BookDao extends JpaRepository<Book, String>, JpaSpecificationExecutor<Book> {

}

Test.java

@Test
    public void test9() {

        List<SysUser> sysUsers = sysUserDao.findAll(new Specification<SysUser>() {
            @Override
            public Predicate toPredicate(Root<SysUser> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
                Path<Object> nickName = root.get("nickName");
                Path<Object> id = root.get("id");

                Predicate predicate = criteriaBuilder.equal(nickName, "嗯嗯");
                Predicate predicate2 = criteriaBuilder.equal(id, "d32478155b6530951cf6b3da56848d5c");
                
                Predicate and = criteriaBuilder.and(predicate2, predicate);
                
                return and;
            }
        });
        sysUsers.forEach(System.out::println);
    }

Test.java

@Test
    public void test10() {
        
        //查询条件
        Specification<SysUser> whereConditions = new Specification<SysUser>() {
            @Override
            public Predicate toPredicate(Root<SysUser> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
                Path<Object> nickName = root.get("nickName");
                Path<Object> id = root.get("id");
                Predicate predicate = criteriaBuilder.like(nickName.as(String.class), "%嗯%");
                Predicate predicate2 = criteriaBuilder.equal(id, "d32478155b6530951cf6b3da56848d5c");
                return criteriaBuilder.or(predicate, predicate2);
            }
        };
        
        //查询后的记录排序问题
        Sort sort = Sort.by(Sort.Direction.DESC, "createTime", "id", "nickName");

        List<SysUser> sysUsers = sysUserDao.findAll(whereConditions, sort);
        sysUsers.forEach(System.out::println);
    }

Test.java

@Test
    public void test11() {

        //查询条件
        Specification<SysUser> whereConditions = new Specification<SysUser>() {
            @Override
            public Predicate toPredicate(Root<SysUser> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
                Path<Object> nickName = root.get("nickName");
                Path<Object> id = root.get("id");
                Predicate predicate = criteriaBuilder.like(nickName.as(String.class), "%嗯%");
                Predicate predicate2 = criteriaBuilder.like(id.as(String.class), "%5%");
                return criteriaBuilder.or(predicate, predicate2);
            }
        };

        //查询后的记录排序问题
        Sort sort = Sort.by(Sort.Direction.DESC, "createTime");
        //分页信息+排序信息
        Pageable pageable = PageRequest.of(2, 2, sort);

        Page<SysUser> page = sysUserDao.findAll(whereConditions, pageable);
        List<SysUser> sysUsers = page.getContent();

        sysUsers.forEach(System.out::println);
    }

三种返回类型 = 根据自己的喜好选择

推荐极力不推荐推荐返回值形式1. JavaBean对象2. Object[]:纯列值数组3. Map:列名Key=》列值Value映射模式

BaseDB.java

@Getter
@Setter
@MappedSuperclass
public class BaseDB {

    private static final long serialVersionUID = 3253505422347170166L;
    /** 主键 - 记录插入自动填充主键处理{@path application.yml} */
    @Id
    private String id;

    @Column(name = "create_time")
    private String createTime;

    @Column(name = "update_time")
    private String updateTime;

    /** 记录是否被逻辑删除:0未删除 1逻辑删除 - 逻辑处理值定义{@path application.yml}*/
    @Column(name = "is_del")
    private Integer isDel;

}

SysUser.java

@Getter
@Setter
@Entity
@Table(name = "sys_user")
public class SysUser extends BaseDB{

    @Column(name = "nick_name")
    String nickName;

    @Column(name = "login_name")
    String loginName;

    @Override
    public String toString() {
        return "SysUser{" +
                "id='" + getId() + '\'' +
                ", nickName='" + nickName + '\'' +
                ", loginName='" + loginName + '\'' +

                '}';
    }
}

SysUserDao.java

@Repository
public interface SysUserDao extends JpaRepository<SysUser, String> {
    
    
    @Query(value = "select * from sys_user limit 5", nativeQuery = true)
    public List<SysUser> getPage();

    /**
     * @return Object[] 里面存储该条数据记录的所有列值
     */
    @Query(value = "select * from sys_user limit 5", nativeQuery = true)
    public List<Object[]> getPage2();

    /**
     *  @return org.springframework.data.jpa.repository.query.AbstractJpaQuery.TupleConverter.TupleBackedMap
     */
    @Query(value = "select * from sys_user limit 5", nativeQuery = true)
    public List<Map> getPage3();

}

Test.java

@Test
    public void test3() {

        System.out.println("\n============JavaBean形式-推荐=========\n");

        List<SysUser> sysUsers = sysUserDao.getPage();
        sysUsers.forEach(System.out::println);

        System.out.println("\n==========Object[]纯列值形式-不推荐===========\n");

        List<Object[]> sysUsers2 = sysUserDao.getPage2();
        sysUsers2.stream()
                .map(Arrays::toString)
                .forEach(System.out::println);

        System.out.println("\n==========Map形式-推荐===========\n");

        List<Map> sysUsers3 = sysUserDao.getPage3();
        sysUsers3.stream()
                .map(HashMap::new)
                .forEach(System.out::println);

    }

修改、删除
@Repository
@Transactional  //增删改需要添加事务支持
public interface BookDao extends JpaRepository<Book, String>, JpaSpecificationExecutor<Book> {

    @Query("update Book set name = ?2 where id = ?1")
    @Modifying  //update需要添加这个注解
    @Rollback(false)  //执行完不回滚 == 如果你加了前两个注解不能修改数据成功,则添加这个注解上去
    public void udpateBookName(String id, String name);

    @Query("delete from Book where id = :id and name = :name")
    @Modifying
    @Rollback(false)  //执行完不回滚 == 如果你加了前两个注解不能修改数据成功,则添加这个注解上去
    public void deleteByIdName(String id, String name);

}

}
@Test
    public void test5() {

        bookDao.udpateBookName("0015ac644213f7db45d63e09c6403395", "测试2");
        
        bookDao.deleteByIdName("0015ac644213f7db45d63e09c6403395", "测试");

        

    }
多表操作
一对多、多对一

SysUser.java

@Getter
@Setter
@Entity
@Table(name = "sys_user")
public class SysUser extends BaseDB implements Serializable {

    @Column(name = "nick_name")
    String nickName;

    @Column(name = "login_name")
    String loginName;

    @OneToMany(targetEntity = ArticleComment.class,fetch = FetchType.EAGER)
    @JoinColumn(name = "user_id", referencedColumnName = "id")
    @NotFound(action= NotFoundAction.IGNORE)
    List<ArticleComment> articleComments;

    @Override
    public String toString() {
        return "SysUser{" +
                "id='" + getId() + '\'' +
                ", nickName='" + nickName + '\'' +
                ", loginName='" + loginName + '\'' +
                ", createTime='" + getCreateTime() + '\'' +
                '}';
    }
}

ArticleComment.java

@Getter
@Setter
@Entity
@Table(name = "article_comment")
public class ArticleComment extends BaseDB implements Serializable {
    /** 版本号 */
    private static final long serialVersionUID = -6282048873013965389L;

    @ManyToOne(targetEntity = SysUser.class, fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @JoinColumn(name ="user_id", referencedColumnName = "id")
    @NotFound(action= NotFoundAction.IGNORE)
    private SysUser sysUser;

    /** 评论所属文章 - article表的id - 逻辑外键关联 */
    @Column(name = "article_id")
    private String articleId;

    /** 父评论id - article_comment表的id - 是否是子评论,为空则不是子评论 - 逻辑外键关联 */
    @Column(name = "parent_comment_id")
    private String parentCommentId;

    /** 评论者 - 用户id - user表的id - 逻辑外键关联 */
    //@Column(name = "user_id")
    //private String userId;

    /** 此条评论回复给谁 - 用户ID */
    @Column(name = "replier_id")
    private String replierId;

    /** 评论内容 */
    @Column(name = "comment_content")
    private String commentContent;

    /** 当前评论被点赞数 */
    @Column(name = "like_num")
    private Integer likeNum;
    /** 当前评论不喜欢数 */
    @Column(name = "not_like_num")
    private Integer notLikeNum;

    @Override
    public String toString() {
        return "ArticleComment{" +
                "id='" + getId() + '\'' +
                ", articleId='" + articleId + '\'' +
                ", parentCommentId='" + parentCommentId + '\'' +
                ", replierId='" + replierId + '\'' +
                ", commentContent='" + commentContent + '\'' +
                ", likeNum=" + likeNum +
                ", notLikeNum=" + notLikeNum +
                '}';
    }
}

SysUserDao.java

@Repository
public interface SysUserDao extends JpaRepository<SysUser, String>, JpaSpecificationExecutor<SysUser> {

    @Query(value = "select * from sys_user limit 5", nativeQuery = true)
    public List<SysUser> getPage();

    /**
     * @return Object[] 里面存储该条数据记录的所有列值
     */
    @Query(value = "select * from sys_user limit 5", nativeQuery = true)
    public List<Object[]> getPage2();

    /**
     *  @return org.springframework.data.jpa.repository.query.AbstractJpaQuery.TupleConverter.TupleBackedMap
     */
    @Query(value = "select * from sys_user limit 5", nativeQuery = true)
    public List<Map> getPage3();

    public SysUser findByIdAndNickName(String id, String nickName);

}

ArticleCommentDao.java

@Repository
public interface ArticleCommentDao extends JpaRepository<ArticleComment, String>, JpaSpecificationExecutor<ArticleComment> {

}
查询
@Test
@Transactional  //懒加载的话,找不到代理的异常,如果立即加载则可以不加
public void test13() {
    List<SysUser> sysUsers = sysUserDao.getPage();
    sysUsers.forEach(sysUser -> {
        List<ArticleComment> articleComments = sysUser.getArticleComments();
        System.out.println(articleComments.size() + ":" + articleComments);
    });
}

@Test
@Rollback(value = false)
@Transactional
public void test16() {
    ArticleComment articleComment = articleCommentDao.getOne("1346e1060e95de36d8d8a7bbc8925dfb");
    SysUser sysUser = articleComment.getSysUser();
    System.out.println(sysUser);
}

插入

主表已含有外键记录

Test.java

@Test
    public void test12() {

        SysUser sysUser = sysUserDao.findById("c08d391e02bc11eb9416b42e99ea3e69").get();

        ArticleComment articleComment = new ArticleComment();
        articleComment.setId(UUID.fastUUID().toString(true));
        articleComment.setCommentContent("test==articleComment");
        articleComment.setSysUser(sysUser);

        articleCommentDao.save(articleComment);
    }

主表未含有外键记录

@Test
    @Rollback(value = false)
    @Transactional
    public void test15() {
        SysUser sysUser = new SysUser();
        sysUser.setId(UUID.fastUUID().toString(true));
        sysUser.setNickName(String.valueOf("爱你test"));

        ArticleComment articleComment = new ArticleComment();
        articleComment.setId(UUID.fastUUID().toString(true));
        

        articleComment.setSysUser(sysUser);
        articleComment.setCommentContent(String.valueOf("test==articleComment"));

        sysUserDao.save(sysUser);
        articleCommentDao.save(articleComment);

    }
多对多
查询

Role.java

@Getter
@Setter
@Entity
@Table(name = "role")
public class Role {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    Long id2;

    @Column(name = "role_name")
    String roleName;

    @ManyToMany(targetEntity = User.class, fetch = FetchType.EAGER)
    @JoinTable(name = "user_role_rel",
            //当前表与中间表的关系
            joinColumns = {@JoinColumn(name = "role_id", referencedColumnName = "id2")},
            //另一个表与中间表的关系
            inverseJoinColumns = {@JoinColumn(name = "user_id", referencedColumnName = "id1")})
    List<User> roles;

}

User.java

@Getter
@Setter
@Entity
@Table(name = "user")
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    Long id1;

    @Column(name = "user_name")
    String userName;

    @ManyToMany(targetEntity = Role.class, fetch = FetchType.EAGER)
    @JoinTable(name = "user_role_rel",
            //当前表与中间表的关系
            joinColumns = {@JoinColumn(name = "user_id", referencedColumnName = "id1")},
            //另一个表与中间表的关系
            inverseJoinColumns = {@JoinColumn(name = "role_id", referencedColumnName = "id2")})
    List<Role> roles;
}

Test.java

@Test
@Rollback(value = false)
@Transactional
public void test17() {
    User user = new User();
    user.setUserName("lrc2");

    Role role = new Role();
    role.setRoleName("游客2");

    //中间表关系维护 == 如果不加中间表的数据为空
    role.setRoles(Arrays.asList(user));

    userDao.save(user);
    roleDao.save(role);

}

相关文章