SpringBoot+Spring JPA基础使用

  1. Spring JPA集成
  2. 基础查询实例
  3. 复杂查询+分页
  4. 一对多查询及多对多查询

1. Spring JPA集成

1.1 项目配置

如截图所示,需要导入对应的组件,其他的和正常创建SpringBoot一致。


需要加入的组件

1.2 配置文件配置

修改application.yml文件进行如下配置:

spring:
  devtools:
    restart:
      enabled: false
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://[IP地址]:3306/test
    hikari:
      username:[MYSQL 用户名]
      password: [MYSQL 密码]
  jpa:
    hibernate:
      #可选参数 
      #create 启动时删数据库中的表,然后创建,退出时不删除数据表 
      #create-drop 启动时删数据库中的表,然后创建,退出时删除数据表 如果表不存在报错 
      #update 如果启动时表格式不一致则更新表,原有数据保留 
      #validate 项目启动表结构进行校验 如果不一致则报错
      ddl-auto: update
    show-sql: true
    properties:
      hibernate:
        enable_lazy_load_no_trans: true

配置文件配置

2. 基础查询实例

2.1 类创建

2.1.1 实体类创建
@Data
@Entity
@Table(name = "user")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "name")
    private String name;
}

对应的注解意思如下:

  • @Data:lombok注解,生成对应的getter(),setter(),有参构造方法,无参构造方法等。
  • @Entity:表明该类是jpa的一个实体
  • @Table:映射到具体的数据库中的表
  • @Id: 主键
  • @GeneratedValue:生成该值的策略
IDENTITY: 采用数据库ID自增长的方式来自增主键字段,Oracle 不支持这种方式; 
AUTO: JPA自动选择合适的策略,是默认选项; 
SEQUENCE: 通过序列产生主键,通过@SequenceGenerator 注解指定序列名,MySql不支持这种方式 
TABLE: 通过表产生主键,框架借由表模拟序列产生主键,使用该策略可以使应用更易于数据库移植。
  • @Column: 显示建立该表的字段连接关系,默认情况会将驼峰命名转成xx_xxx进行映射。
2.1.2 Dao实体创建
public interface UserDao extends JpaRepository<User,Long>, JpaSpecificationExecutor<User> {

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

    public User findByName(String name);
}

对应继承的类作用:

  • JpaRepository<实体类型,主键类型>:封装了基础的CURD操作。
  • JpaSpecificationExecutor<操作的实体类类型>:封装了复杂的查询(分页)

对应的操作类型:

  • SQL 语句查询:通过@Query注解,​ nativeQuery : false(使用jpql查询) | true(使用本地查询 :sql查询),第一个参数则为?1,第二个参数则为?2以此类推。
  • 方法命名规则查询
/**
     * 方法名的约定:
     *      findBy : 查询
     *            对象中的属性名(首字母大写) : 查询的条件
     *            CustName
     *            * 默认情况 : 使用 等于的方式查询
     *                  特殊的查询方式
     *
     *  findByCustName   --   根据客户名称查询
     *
     *  再springdataJpa的运行阶段
     *          会根据方法名称进行解析  findBy    from  xxx(实体类)
     *                                      属性名称      where  custName =
     *
     *      1.findBy  + 属性名称 (根据属性名称进行完成匹配的查询=)
     *      2.findBy  + 属性名称 + “查询方式(Like | isnull)”
     *          findByCustNameLike
     *      3.多条件查询
     *          findBy + 属性名 + “查询方式”   + “多条件的连接符(and|or)”  + 属性名 + “查询方式”
     */

2.2 测试

2.2.1 简单测试保存
 @Autowired
 private UserDao userDao;

 @Test
 void simpleSave(){
     User user = new User();
     user.setName("测试2");
     userDao.save(user);
  }
2.2.2 简单测试查询
    @Test
    void sqlSearch(){
        List<User> users = userDao.findSqlByLikeName("测试2");
        System.out.println(users);

    }

    @Test
    void matchSearch(){
        User user = userDao.findByName("测试");
        System.out.println(user);
    }

3. 复杂查询+分页

有时我们在查询某个实体的时候,给定的条件是不固定的,这时就需要动态构建相应的查询语句,在Spring Data JPA中可以通过JpaSpecificationExecutor接口查询。相比JPQL,其优势是类型安全,更加的面向对象。

  • JpaSpecificationExecutor方法列表
1. T findOne(Specification spec); //查询单个对象

2. List findAll(Specification spec); //查询列表

3. 查询全部,分页

    pageable:分页参数

    返回值:分页pageBean(page:是springdatajpa提供的)

    Page findAll(Specification spec, Pageable pageable);

4.查询列表

    Sort:排序参数

    List findAll(Specification spec, Sort sort);

5. long count(Specification spec);//统计查询

我们需要自己实现Specification:

//root:查询的根对象(查询的任何属性都可以从根对象中获取)
//CriteriaQuery:顶层查询对象,自定义查询方式(了解:一般不用)
//CriteriaBuilder:查询的构造器,封装了很多的查询条件
Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb); //封装查询条件

测试

3.1 简单的例子
@Test
    void testSpec(){
        Specification<User> spec = new Specification<User>() {
            @Override
            public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                //1.获取比较的属性
                Path<String> name = root.get("name");
                Path<Long> id = root.get("id");
                //2.构造查询条件  :    select * from user where name = '测试1' and id = 1
                /**
                 * 第一个参数:需要比较的属性(path对象)
                 * 第二个参数:当前需要比较的取值
                 */
                Predicate predicate1 = cb.equal(name, "测试2");//进行精准的匹配  (比较的属性,比较的属性的取值)
                Predicate predicate2 = cb.equal(id,1);
                return cb.and(predicate1,predicate2);
            }
        };
        User user = userDao.findOne(spec).get();
        System.out.println(user);


    }
3.2 分页的例子

通过Sort进行排序,PageRequest 进行分页,第一个参数为当前查询的页码(从0开始),第二个参数为每页查询的数量,第三个参数为排序字段。
findAll()支持Specification查询和分页,这样就可以实现复杂类型的查询。

    @Test
    void testPage(){
        Specification spe = null;
        Sort sort = Sort.by(Sort.Direction.DESC,"id");
        PageRequest pageRequest = PageRequest.of(0,2,sort);

        Page<User> users = userDao.findAll(spe,pageRequest);
        System.out.println(users.getTotalPages());
        System.out.println(users.getContent());

    }

4. 一对多查询及多对多查询

假设:用户和班级的关系为:1:N,一个用户可以在不同的班级内。
假设:用户和角色的关系为:N:M,多对多的关系在中间表维护

4.1 类改造及新增

4.1.1 User类改造
@Data
@Entity
@Table(name = "user")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

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

    @OneToMany(mappedBy = "user",cascade = CascadeType.ALL)
    @org.hibernate.annotations.ForeignKey(name = "none")
    private List<ClassE> classE = new ArrayList<>();

    @ManyToMany(targetEntity = Role.class,cascade = CascadeType.ALL)
    @JoinTable(name = "sys_user_role",
            //joinColumns,当前对象在中间表中的外键
            joinColumns = {@JoinColumn(name = "sys_user_id",referencedColumnName = "id")},
            //inverseJoinColumns,对方对象在中间表的外键
            inverseJoinColumns = {@JoinColumn(name = "sys_role_id",referencedColumnName = "id")}
    )
    private List<Role> roles = new ArrayList<>();
}
4.1.2 ClassE类创建
@Entity
@Table(name = "class")
@Getter
@Setter
public class ClassE {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

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


    /**
     * 配置联系人到客户的多对一关系
     *     使用注解的形式配置多对一关系
     *      1.配置表关系
     *          @ManyToOne : 配置多对一关系
     *              targetEntity:对方的实体类字节码
     *      2.配置外键(中间表)
     *
     * * 配置外键的过程,配置到了多的一方,就会在多的一方维护外键
     *
     */
    @ManyToOne(targetEntity = User.class,fetch = FetchType.EAGER)
    @JoinColumn(name = "uid",referencedColumnName = "id",foreignKey = @ForeignKey(value = ConstraintMode.NO_CONSTRAINT))
    private User user;
}
4.1.3 Role类创建
@Entity
@Getter
@Setter
@Table(name = "role")
public class Role {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Long id;
    @Column(name = "name")
    private String name;

    //配置多对多
    @ManyToMany(mappedBy = "roles")  //配置多表关系(对方配置映射关系的属性)
    private List<User> users = new ArrayList<>();
}
4.1.4 Dao分别创建
public interface UserDao extends JpaRepository<User,Long>, JpaSpecificationExecutor<User> {

}

public interface RoleDao extends JpaRepository<Role,Long>, JpaSpecificationExecutor<Role> {

}

public interface ClassDao extends JpaRepository<ClassE,Long>, JpaSpecificationExecutor<ClassE> {

}

4.2 测试

@Test
    @Transactional
    @Rollback(false)
    void testRoleAdd(){
        User user = new User();
        user.setName("测试人员");
        ClassE classE = new ClassE();
        classE.setName("一班");
        ClassE classE1 = new ClassE();
        classE.setName("二班");
        user.getClassE().add(classE);
        user.getClassE().add(classE1);

        classE.setUser(user);
        classE1.setUser(user);

        Role role = new Role();
        role.setName("角色1");
        Role role1 = new Role();
        role1.setName("角色2");
        user.getRoles().add(role);
        user.getRoles().add(role1);
        userDao.save(user);
        classDao.save(classE);
        classDao.save(classE1);
        roleDao.save(role);
        roleDao.save(role1);
    }
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342