Spring Boot整合Spring Data JPA学习笔记

Spring Boot 整合Spring Data JPA

ORM思想

主要目的:操作实体类相当于操作数据库表
建立两个关系:
1.建立实体类和表的映射关系
2.建立实体类中的属性和表中字段的映射关系

不再重点关注sql语句(这是ORM思想我感觉最好的一个地方)

实现了ORM思想的框架:mybatis,hibernate
JPA底层实现数据库语言的还是hibernate

Spring Boot 整合Spring Data JPA 的时候Dao层的repository接口类继承的四个接口的详解

Spring Boot 整合Spring Data JPA 的时候需要定义Dao 层,就是repository类,这个类是一个接口,来继承一个接口,一共有四个接口

1.Repository接口

提供了方法名称命名查询方式
提供了基于@Query注解查询与更新

下面是repository类的代码

public interface PersonRepositoryByName extends Repository<Person,Integer> {
    //一定注意下方法名:findBy(关键字)+属性名(首字母大写)
    List<Person> findByName(String name);//按照姓名查找
    List<Person> findByAddress(String address);//按照地址查找
    List<Person> findByNameOrAge(String name,Integer age);//按照姓名或者年龄查找
    List<Person> findByNameLike(String name);//按照姓名模糊查找
    List<Person> findByAddressLike(String address);//按照地址模糊查找
}

下面是一个测试代码

@SpringBootTest
class SpringbootSpringdatajpaApplicationTests {
    @Autowired
    PersonRepository personRepository;
    @Autowired
    PersonRepositoryByName personRepositoryByName;

    @Test
    public void testSave() {
        Person person = new Person();
        person.setAge(24);
        person.setName("张三");
        person.setAddress("济南市槐荫区");
        this.personRepository.save(person);
    }

    @Test
    public void testFindByName() {
        List<Person> list = this.personRepositoryByName.findByName("张三");
        for (Person persons : list) {
            System.out.println(persons);
        }
    }

    @Test
    public void testFindByAddress() {
        List<Person> list = this.personRepositoryByName.findByAddress("济南市历下区");
        for (Person persons : list) {
            System.out.println(persons);
        }
    }
    @Test
    public void testFindByNameOrAge() {
        List<Person> list = this.personRepositoryByName.findByNameOrAge("李四",21);
        for (Person persons : list) {
            System.out.println(persons);
        }
    }
    @Test
    public void testFindByNameLink() {
        List<Person> list = this.personRepositoryByName.findByNameLike("李%");
        for (Person persons : list) {
            System.out.println(persons);
        }
    }
    @Test
    public void testFindByAddressLink() {
        List<Person> list = this.personRepositoryByName.findByAddressLike("济南市%");
        for (Person persons : list) {
            System.out.println(persons);
        }
    }
}

基于@Query注解查询与更新

首先还是需要建立一个repository接口来继承Repository接口,然后写方法

代码

public interface PersonRepositoryQueryAnnotation extends Repository<Person,Integer> {
    //使用@Query注解结合sql语句来实现查找,**注意**:?的顺序和形参的顺序是一一对应的
    nativeQuery=true的意思是告诉hibernate这是一个标准的sql语句
    @Query(value = "select * from tb_person where name=?",nativeQuery = true)
    List<Person> queryByName(String name);
    @Query(value = "update tb_person set name=? where id=?",nativeQuery = true)
    @Modifying//这是更新的注解与@Query注解配合使用来更新
    void updateNameById(String name,Integer id);
}

测试代码:

@Test
public void testQueryByName() {
    List<Person> list = this.personRepositoryQueryAnnotation.queryByName("张三");
    for (Person persons : list) {
        System.out.println(persons);
    }
}
@Test
@Transactional//手动设置一个事务,更新操作必须在事务中
@Rollback(value = false)//取消自动回滚
public void testUpdateNameById() {
    this.personRepositoryQueryAnnotation.updateNameById("张三三",3);
    }
}
2.CrudRepository接口
注意:CrudRepository接口是继承的Repository接口的,主要功能就是增删改查,而且CrudRepository接口的使用和JpaRepository接口的使用非常类似

CrudRepository接口中的方法都有[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YIAb4NvH-1618480041802)(C:\Users\86130\AppData\Roaming\Typora\typora-user-images\image-20210413213804466.png)]

接口类的实现代码

public interface CrudRepository extends org.springframework.data.repository.CrudRepository<Person,Integer> {


}

测试增删改查的代码

//插入数据
@Test
public void testInsert() {
    Person person=new Person();
    person.setName("张三丰");
    person.setAddress("青岛市");
    person.setAge(333);
    crudRepository.save(person);
    }
//注意CrudRepository的save方法既可以插入又可以更新
@Test
public void testInsert() {
    Person person=new Person();//**注意**:如果你想要更新的话必须指定id,使用setId()方法
    person.setId(4);
    person.setName("张三丰");
    person.setAddress("青岛市黄岛区");
    person.setAge(333);
    crudRepository.save(person);
    }
//按照Id进行指定删除
@Test
public void testDeleteById() {
    crudRepository.deleteById(5);
}
//按照指定Id进行查找
@Test
public void testFindById() {
    Optional<Person> persons = crudRepository.findById(4);
    System.out.println(persons);
}
//查找全部
@Test
    public void testFindAll() {
        Iterable<Person> all = crudRepository.findAll();
        System.out.println(all);

    }
3.PagingAndSortingRepository接口

这个类继承了CrudRepository接口,主要功能是排序查询和分页查询

首先我们看一下接口类的实现

public interface RepositoryPagingAndSorting extends PagingAndSortingRepository<Person,Integer> {
}

PagingAndSortingRepository接口中的方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-geuBbPid-1618480041806)(C:\Users\86130\AppData\Roaming\Typora\typora-user-images\image-20210414165917747.png)]

测试代码

//排序查询
@Test
 public void testSort() {
     //Order来定义排序规则DESC是降序,ASC是升序
Order order=new Order(Sort.Direction.DESC,"id");
     //spring boot2.0以上版本的Sort重写了,不能new Sort了,要用Sort.by
 Sort sort=Sort.by(order);
     List<Person> persons = (List<Person>) repositoryPagingAndSorting.findAll(sort);
     for (Person list: persons
          ) {
         System.out.println(list);
     }
 }
//分页查询
 @Test
 public void testPaging() {
     //spring boot2.0以上版本不能用Pageable了要使用PageRequest.of,返回PageRequest
PageRequest pageable=PageRequest.of(1,2);
     Page<Person> page = repositoryPagingAndSorting.findAll(pageable);
     System.out.println("总条数"+page.getTotalElements());
     System.out.println("总页数"+page.getTotalPages());
     List<Person> list = page.getContent();
     for (Person persons:list
          ) {
         System.out.println(persons);
     }

 }
//分页排序查询(将上面两个方法结合)
 @Test
 public void testPagingAndSort() {
    Order order=new Order(Sort.Direction.DESC,"id");
    Sort sort=Sort.by(order);
    PageRequest pageable= PageRequest.of(0,2,sort);
     Page<Person> page = repositoryPagingAndSorting.findAll(pageable);
     System.out.println("总条数"+page.getTotalElements());
     System.out.println("总页数"+page.getTotalPages());
     List<Person> list = page.getContent();
     for (Person persons:list
          ) {
         System.out.println(persons);
     }

 }

4.JpaRepository接口

该类继承了PagingAndSortingRepository接口,可以对父接口中的方法返回值进行适配

接口类的定义

public interface PersonRepository extends JpaRepository<Person,Integer>{
}

JpaRepository接口中的方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TZiSoi0D-1618480041807)(C:\Users\86130\AppData\Roaming\Typora\typora-user-images\image-20210414171553284.png)]

测试代码

//分页查询
@Test
public void testJpaRepository() {
    Sort sort=Sort.by(Sort.Direction.DESC,"id");
    PageRequest pageable=PageRequest.of(0, 2,sort);
    Page<Person> page = personRepository.findAll(pageable);
    List<Person> list = page.getContent();
    for (Person persons:list
         ) {
        System.out.println(persons);
    }

}
//按照顺序查询
    @Test
    public void testJpaRepositoryFind() {
        Sort sort = Sort.by(Sort.Direction.DESC, "id");
        //注意这里的personRepository.findAll(sort)不需要进行强制类型转换,因为JpaRepository实现了对父接口的返回值类型的适配
        List<Person> list = personRepository.findAll(sort);
        for (Person persons : list
        ) {
            System.out.println(persons);
        }

    }

=5.JpaSpecificationExecutor接口==

JpaSpecificationExecutor没有继承前面的四个接口的任意一个,它是独立的一个,所以它有时候需要配合其他的接口使用。JpaSpecificationExecutor接口主要用于对一些复杂条件的查询

JpaSpecificationExecutor接口经常配合JpaSpecificationExecutor接口使用

下面是JpaSpecificationExecutor接口中的方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xKYO59kz-1618480041809)(C:\Users\86130\AppData\Roaming\Typora\typora-user-images\image-20210414191340758.png)]

接口类的代码

public interface RepositoryJpaSe extends JpaSpecificationExecutor<Person>/*只需要写实体类*/,JpaRepository<Person,Integer>{
}

测试代码

//单条件查询
@Test
public void testJpaSe() {
    Specification spe=new Specification() {
        @Override
  			/**root是我们实体类的一个封装类
  			*	criteriaQuery:封装了我们要执行的查询中的各个部分的信息
  			*	criteriaBuilder:定义不同的查询条件
  			*/
        public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder criteriaBuilder) {
            Predicate predicate = criteriaBuilder.equal(root.get("name"), "张三三");
            return predicate;
        }
    };
    Optional list = repositoryJpaSe.findOne(spe);
    System.out.println(list);
}
//多条件查询
@Test
public void testJpaSpe() {
    Specification spe=new Specification() {
        @Override
        public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder criteriaBuilder) {
            //多条件查询的时候我们就将我们的多个查询条件放进Predicate类型的List中
            Predicate name = criteriaBuilder.equal(root.get("name"), "李四");
            Predicate age = criteriaBuilder.equal(root.get("age"), 24);
            List<Predicate> list=new ArrayList<>();
            list.add(name);
            list.add(age);
            Predicate[] pre=new Predicate[list.size()];
            //将list中的数据注入到一个Predicate类型的数组中,其中or是或者的关系,and是并列的关系
            return criteriaBuilder.or(list.toArray(pre));
        }
    };
    List list = repositoryJpaSe.findAll(spe);
    System.out.println(list);

}
//多条件查询并且排序
  @Test
    public void testJpaSpe1() {
        Specification spe=new Specification() {
            @Override
            public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder criteriaBuilder) {
                //注意可以这样来写我们的多个条件,这样写比较方便简洁
                return criteriaBuilder.or(criteriaBuilder.equal(root.get("name"),"张三丰"),criteriaBuilder.equal(root.get("age"),333),criteriaBuilder.equal(root.get("id"),2));

            }
        };
        Sort sort=Sort.by(Sort.Direction.DESC,"id");
        List list = repositoryJpaSe.findAll(spe, sort);
        System.out.println(list);
    }

//多条件查询并且分页查询
@Test
public void testJpaSpe2() {
    Specification spe=new Specification() {
        @Override
        public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder criteriaBuilder) {
            //注意可以将and和or条件组合使用
            return criteriaBuilder.or(criteriaBuilder.equal(root.get("name"),"张三丰"),criteriaBuilder.equal(root.get("age"),333),criteriaBuilder.equal(root.get("id"),2));

        }
    };
    Sort sort=Sort.by(Sort.Direction.DESC,"id");
    PageRequest pageable=PageRequest.of(0,1,sort);
    Page<Person> people = repositoryJpaSe.findAll(spe,pageable);
    List<Person> list = people.getContent();
    System.out.println(list);
}

关联映射操作

一对多的关联映射

要在一对多和多对一的实体类中分别加注解
实体类代码
//roles   角色的实体类
@Entity
@Table(name="tb_roles")

public class Roles {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name="roles_id")
    private Integer roles_id;
    @Column(name="role_name")
    private String roles_name;
    /**OneToMany:这个实体类是一个对应多个,一个角色分配给多个用户,所以对于这个实体类来说就是一对多
    *mappedBy:
    *fetch=FetchType.EAGER:关闭懒加载,否则的话会报错,说创建会话失败
    */
    @OneToMany(mappedBy = "roles",fetch=FetchType.EAGER)
    //对应多个Person对象所以就用一个set集合来存放多个Person对象
    private Set<Person> persons=new HashSet<>();
     public Set<Person> getPersons() {
        return persons;
    }

    public void setPersons(Set<Person> persons) {
        this.persons = persons;
    }



    public Integer getRoles_id() {
        return roles_id;
    }

    public void setRoles_id(Integer roles_id) {
        this.roles_id = roles_id;
    }

    public String getRoles_name() {
        return roles_name;
    }

    public void setRoles_name(String roles_name) {
        this.roles_name = roles_name;
    }    @Override
    public String toString() {
        return "Roles{" +
                "roles_id=" + roles_id +
                ", roles_name='" + roles_name + '\'' +
                '}';
    }
   
//person   用户的实体类
@Entity
@Table(name="tb_person")

public class Person {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Integer id;
    @Column(name = "age")
    private Integer age;
    @Column(name = "name")
    private String name;
    @Column(name = "address")
    private String address;
    //多个角色对应一个用户,所以用ManyToOne
    //cascade = CascadeType.PERSIST:开启级联:创建角色的时候也创建用户
     @ManyToOne(cascade = CascadeType.PERSIST)
    //@JoinColumn:外键关联,person实体类中和roles关联的属性为rolesId
     @JoinColumn(name = "rolesId")
     private Roles roles;


    public Roles getRoles() {
        return roles;
    }

    public void setRoles(Roles roles) {
        this.roles = roles;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "Person{" +
                "id=" + id +
                ", age=" + age +
                ", name='" + name + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
    }

测试代码

@SpringBootTest
public class OneToManyTest {
        @Autowired
    PersonRepository repository;
        @Test
    /*添加的步骤如下:
    *  1.创建角色
    *	2.创建用户
    *	3.建立关联
    *	4.保存
    */
    public void testOneToMany(){
            //创建一个角色
            Roles roles=new Roles();
            roles.setRoles_name("鬼剑士");
            //创建一个用户
            Person person=new Person();
            person.setAge(19);
            person.setAddress("济南市莱芜区");
            person.setName("李小龙");
            //设置关系
            roles.getPersons().add(person);
        //一对多的关系中的时候,多对一的哪=那一方就用set方法,一对多的那一方就用get .add方法
            person.setRoles(roles);
        //要开启person实体类的级联关系
        repository.save(person);
    }

    //查询操作
    @Test
    public void testOneToManyFind(){
        //按照person的id查找person,然后获取person中的roles,注意,在这个过程中执行 System.out.println语句后其实已经关闭了person这个实体类,所以我们需要关闭hibernate的懒加载
        Optional<Person> person = repository.findById(7);
        System.out.println(person);
        Roles roles = person.get().getRoles();
        System.out.println(roles.getRoles_name());
    }
}

多对多的关系

实体类代码
//roles    角色的实体类
@Entity
@Table(name="tb_roles")

public class Roles {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name="roles_id")
    private Integer roles_id;
    @Column(name="role_name")
    private String roles_name;
    /* @ManyToMany:多对多的关系
    *cascade = CascadeType.PERSIST:开启级联操作,创建角色的时候也创建武器
    *fetch = FetchType.EAGER:关闭懒加载
    */
    @ManyToMany(cascade = CascadeType.PERSIST,fetch = FetchType.EAGER)
    /*
    *@JoinTable:创建一个中间表,用来映射这两个多对多的实体类
    *name:指定中间表的名称
   	*joinColumns:当前实体类和中间表中外键关联的属性,joinColumns = @JoinColumn(name="roles_id"):当前这个实体类中的外	*键和中间表中的roles_id对应
   	*inverseJoinColumns:另一个实体类中的外键对应的属性
    */
    @JoinTable(name="tb_roles_weapons",joinColumns = @JoinColumn(name="roles_id"),inverseJoinColumns =@JoinColumn(name="weapons_id"))
    private Set<Weapons> weapons=new HashSet<>();
    
    public Integer getRoles_id() {
        return roles_id;
    }

    public void setRoles_id(Integer roles_id) {
        this.roles_id = roles_id;
    }

    public String getRoles_name() {
        return roles_name;
    }

    public void setRoles_name(String roles_name) {
        this.roles_name = roles_name;
    }

    public Set<Weapons> getWeapons() {
        return weapons;
    }

    public void setWeapons(Set<Weapons> weapons) {
        this.weapons = weapons;
    }

    @Override
    public String toString() {
        return "Roles{" +
                "roles_id=" + roles_id +
                ", roles_name='" + roles_name + '\'' +
                '}';
    }
}

//weapons    武器的实体类
@Entity
@Table(name="tb_weapons")
public class Weapons {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name="id")
    private Integer id;
    @Column(name="name")
    private String name;
    @Column(name="price")
    private Integer price;
    //多对多的操作中,在另一个实体类中只需要用一个@ManyToMany注释就好了
    //mappedBy:这个属性的值就是我们当前实体类对应的另一个实体类中的对应的当前这个实体类的属性
    @ManyToMany(mappedBy = "weapons")
    private Set<Roles> roles=new HashSet<>();

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getPrice() {
        return price;
    }

    public void setPrice(Integer price) {
        this.price = price;
    }

    public Set<Roles> getRoles() {
        return roles;
    }

    public void setRoles(Set<Roles> roles) {
        this.roles = roles;
    }

    @Override
    public String toString() {
        return "Weapons{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", price=" + price +
                '}';
    }
}

测试代码

/**
*保存的步骤:
*1.建立角色实体类
*2.建立武器实体类
*3.建立关系
*4.保存
*/
@SpringBootTest
public class ManyToManyTest {
    @Autowired
    RepositoryRolesAndWeapons repositoryRolesAndWeapons;
    @Test
    public void testSave(){
        //建立角色
        Roles r1=new Roles();
        r1.setRoles_name("圣职者");
        //建立武器
        Weapons w1=new Weapons();
        w1.setName("十字架");
        w1.setPrice(500);
        //建立联系
        r1.getWeapons().add(w1);
        w1.getRoles().add(r1);
        //保存
        repositoryRolesAndWeapons.save(r1);
    }

    @Test
    public void testFind(){
        Optional<Roles> roles = repositoryRolesAndWeapons.findById(2);
        System.out.println(roles);
        //注意不要忘记关闭懒加载
        Set<Weapons> weapons=roles.get().getWeapons();
        for (Weapons weapon:weapons
             ) {
            System.out.println(weapon);
        }
    }
}

jpa的主键生成策略

1.GenerationType.IDENTITY 自增(底层数据库必须支持自增:比如mysql数据库)

2.GenerationType.SEQUENCE 序列(底层数据库必须支持序列:比如oracle数据库)

3.GenerationType.TABLE 自增(jpa提供的一种机制,通过一张数据表的形式帮助我们完成主键自增)

会自动生成一张另外的表帮助我们来实现主键自增

4.GenerationType.AUTO :由程序自动帮助我们选择使用哪种主键生成策略

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值