SpringDataJpa详解

JpaRepository这个是最重要的方法,从父接口中所继承的方法对返回值做适配处理。

JpaRepository层次结构图

SimpleJpaRepository是JpaRepository的实现类,如果想进行扩展可以继承此类。

这个类是核心的类,想了解jpa执行过程需要对这个类进行debug,同时也是Spring Jpa动态代理的实现类

@RepositoryDefinition

是用注解方式声明继承Repository接口,和继承Repository接口的方式等价

@NoRepositoryBeanInterface

相当于你在使用spring data jpa 的时候,每个实体类有需要实现的相同的方法,就可以单独抽取出来,放在一个公共的接口MyRepository中,并这个类继承了jpa的相关Repository接口或类,由MyRepository接口来衔接jpa的相关操作,其他实体类需要实现的操作就直接继承MyRepository接口,不用每次都去继承jpa的相关接口或类,所以这个公共接口就需要这个注解@NoRepositoryBean来标识

https://blog.csdn.net/qq_39818325/article/details/86653947

一、定义查询方法的配置方法

1.Spring Jpa Repository的实现原理是采用动态代理的机制,

@EnableJpaRepositories可以配置方法的查询策略,一般不需要配置,可以规定使用方法指定,也可以使用注解方式,或者两者混合,默认两者混合。

查询方法关键字列表:

可以通过查看PartTree源码来确定表达式,然后确定

2.方法查询策略的属性表达式

Person(private Address addr(private String zipCode))可以使用属性表达式

但是如果主属性中有重复的属性名建树就会失败。

解决方案如下

3.查询结果的处理

这里使用Slice不会执行count,只是知道之后有没有Slice

这里使用first和top来对结果返回行数进行限制。

查询结果不同形式有:List/Stream/Page/Future

3.projections对查询结果的扩展

可以使用数据投影对结果进行操作,也就是对获取的列进行选取

@ToString
@NoArgsConstructor
@Setter
@Getter
@Entity
@Table(name = "tb_employee")
public class Emp {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Long id;
    @Column(name = "name",length = 100)
    private String name;
    @Column(name = "age",length = 3)
    private int age;
}

public interface EmpProjections {
    String getName();
}

public interface EmpReposiroty extends JpaRepository<Emp,Long> {
    Collection<EmpProjections> findByNameContains(String str);
}


    @Test
    public void test5(){
        this.empReposiroty.findByNameContains("hello").stream().forEach(e->{
            System.out.println(e.getName());
        });
    }
//hello1
//hello2
//hello1
//hello2

可以使用spel表达式

4.实现机制

通过QueryExecutorMethodInterceptor这个类的源代码,这个类实现了MethodInterceptor这个接口,也就是说这是一个方法拦截器,当@Repository上的查询方法被调用时,会执行invoke方法。

过程如下

5.注解式查询

@Query排序

@Query分页

对原生sql的支持

@Param起别名

@MappedSuperclass:

https://blog.csdn.net/qq_527235890/article/details/70196396

1.标识符注解在父接口上面,是用来标识父类的

2.表示其不能映射到数据库表中,因为其不是一个完整的实体类,但是它拥有的属性能够映射到其子类所用的数据库表中

3.不能和@Entity和@Table注解混用

@Modifying修改标记允许update

@QueyHints一种老旧是数据库查询

@Procedure存储过程的查询方法

@NamedQueries预定义查询

@Query>@NameQuery>方法自定义查询

三、@Entity实例里面常用注解详解

1.基本注解

@Entity

@Table

@Id定义属性为数据库的主键,一个实体里面必须有一个

@IdClass利用外部类的联合主键

[1]必须实现Serializable接口

[2]必须有默认public无参数的构造方法

[3]必须覆盖equals和hashCode方法

@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode
@Getter
@Setter
public class UserBlogKey implements Serializable {
    private String title;
    private Integer createUserId;
    private LocalDateTime createTime;
}

@ToString
@NoArgsConstructor
@Getter
@Setter
@Entity
@Table(name = "tb_userblog")
@IdClass(value = UserBlogKey.class)
public class UserBlogEntity {
    @Column(name = "uuid")
    private String uuid;
    @Id
    @Column(name = "title",nullable = false)
    private String title;
    @Id
    @Column(name = "create_user_id",nullable = false)
    private Integer createUserId;
    @Id
    @Column(name = "create_time",nullable = false)
    private LocalDateTime createTime;
}

public interface UserBlogRepository extends JpaRepository<UserBlogEntity,UserBlogKey> {
}

@GeneratedValue指定主键生成策略

@Basic表示属性到数据库的映射,如果实体字段上没有任何注解,默认为@Basic

@Transient非持久化属性,数据库映射的时间忽略

@Column

@Temporal设置Date类型的属性映射到对应精度的字段

TemporalType.DATE/TemporalType.TIME/TemporalType.TIMESTAMP

@Emumerated直接映射enum枚举类型的字段

@ToString
@NoArgsConstructor
@Setter
@Getter
@Entity
@Table(name = "tb_user")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private long id;
    @Column(name = "name")
    private String name;
    //注意映射的值~!
    @Enumerated(EnumType.STRING)
    @Column(name = "gender")
    private Gender gender;
}

@AllArgsConstructor
@Getter
public enum Gender {
    MAIL("男性"),FMAIL("女性");
    private String name;
}

public interface UserRepository extends JpaRepository<User,Long> {
}

@Lob映射成clob或者blob

映射的组合使用

2.关联注解

@JoinColum

@JoinColums定义多个字段的关联关系

@OneToOne

@ToString(exclude = "department")
@NoArgsConstructor
@Setter
@Getter
@Entity
@Table(name = "tb_employee")
public class Emp {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Long id;
    @Column(name = "name",length = 100)
    private String name;
    @Column(name = "age",length = 3)
    private int age;
    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn
    private Department department;
}

@ToString(exclude = "emp")
@NoArgsConstructor
@Setter
@Getter
@Entity
@Table(name = "tb_department")
public class Department {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Long id;
    @Column(name = "name",length = 100)
    private String name;
    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn
    private Emp emp;
}

public interface EmpReposiroty extends JpaRepository<Emp,Long> {
    Collection<EmpProjections> findByNameContains(String str);
}
public interface DepartmentRepository extends JpaRepository<Department,Long> {
}


    @Autowired
    EmpReposiroty empReposiroty;
    @Autowired
    DepartmentRepository departmentRepository;

    @Transactional
    @Rollback(false)
    @Test
    public void test() {
        //级联关系的建立比如互入
        Emp e1 = new Emp();
        e1.setName("hello1");
        e1.setAge(22);
        Department d1 = new Department();
        d1.setName("world1");
        d1.setEmp(e1);
        e1.setDepartment(d1);
        Emp e2 = new Emp();
        e2.setName("hello2");
        e2.setAge(23);
        Department d2 = new Department();
        d2.setName("world2");
        d2.setEmp(e2);
        e2.setDepartment(d2);
        //在主键维护方插入,且开启级联操作
        this.empReposiroty.save(e1);
        this.empReposiroty.save(e2);
        System.out.println(this.empReposiroty.findAll());
    }

    @Test
    public void test1() {
        Optional<Emp> e1 = this.empReposiroty.findById(1l);
        System.out.println(e1.get() + " " + e1.get().getDepartment());
        Optional<Department> d1 = this.departmentRepository.findById(1l);
        System.out.println(d1.get() + " " + d1.get().getEmp());
    }

@OneToMany/@ManyToOne

只有关系维护方才能操纵两者的关系

@ToString(exclude = "lib")
@NoArgsConstructor
@Setter
@Getter
@Entity
@Table(name = "tb_book")
public class Book {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Long id;
    @Column(name = "name")
    private String name;
    @Column(name = "page")
    private int page;
    @Column(name = "description")
    private String description;
    @ManyToOne(cascade = CascadeType.PERSIST)
    @JoinColumn
    private Library lib;
}

@ToString(exclude = "books")
@NoArgsConstructor
@Setter
@Getter
@Entity
@Table(name = "tb_library")
public class Library {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Long id;
    @Column(name = "name")
    private String name;
    @Column(name = "description")
    private String description;
    //这里mappedBy指的是关系维护的属性,也就是少的一方的属性
    //默认的懒加载就是没有左联
    @OneToMany(cascade = CascadeType.REMOVE,mappedBy = "lib")
    private Set<Book> books=new HashSet<>();
}

public interface BookRepository extends JpaRepository<Book,Long> {
    List<Book> findBooksByLib_Id(Long libId);
}

public interface LibraryRepository extends JpaRepository<Library,Long> {
}

 @Autowired
    BookRepository bookRepository;

    @Autowired
    LibraryRepository libraryRepository;

    @Test
    @Transactional
    @Rollback(false)
    public void test2() {
        //1.建立对象 2.生成关系
        Library library = new Library();
        library.setName("president");
        library.setDescription("For President!");
        Book book1 = new Book();
        book1.setName("hero");
        book1.setPage(100);
        book1.setDescription("To be a hero");
        Book book2 = new Book();
        book2.setName("soldier");
        book2.setPage(200);
        book2.setDescription("Death!Frighting!");
        library.setBooks(new HashSet() {{
            add(book1);
            add(book2);
        }});
        book1.setLib(library);
        book2.setLib(library);
        this.libraryRepository.save(library);

        book1.setLib(library);
        book2.setLib(library);
        this.bookRepository.save(book1);
        this.bookRepository.save(book2);
    }

    @Test
    @Transactional
    @Rollback(false)
    public void test3() {
        this.libraryRepository.deleteById(1l);
    }

    @Test
    public void test4() {
        this.bookRepository.findAll().stream().forEach(e -> {
            System.out.println(e + " " + e.getLib());
        });
        //oneToMany端懒加载是拉不出来,所以要求我们必须拉取出来
        //如果进行激进加载,就是连表查询返回结果集
        this.libraryRepository.findAll().stream().forEach(e->{
            System.out.println(e+" "+this.bookRepository.findBooksByLib_Id(e.getId()));
        });
//        this.libraryRepository.findAll().stream().forEach(e->{
//            System.out.println(e+" "+e.getBooks());
//        });
    }
    @Test
    public void test5(){
        this.empReposiroty.findByNameContains("hello").stream().forEach(e->{
            System.out.println(e.getName());
        });
    }

@OrderBy关联查询时排序

@JoinTable关联关系表

@ManyToMany

@Getter
@Setter
@NoArgsConstructor
@ToString(exclude = "roles")
@Entity
@Table(name = "tb_menu")
public class Menu {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Long id;
    @Column(name = "name")
    private String name;
    @Column(name = "url")
    private String url;
    //Role那一侧关联的映射对象属性
    @ManyToMany(mappedBy = "menus",cascade = CascadeType.ALL)
    Set<Role> roles=new HashSet<>();
}

@Getter
@Setter
@NoArgsConstructor
@ToString(exclude = "menus")
@Entity
@Table(name = "tb_role")
public class Role {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private long id;
    @Column(name = "name")
    private String name;
    @ManyToMany(cascade = CascadeType.REMOVE)
    //JoinTable:中间表,两侧任意一方就可以
    //joinColumns:建立当前表在中间表中的外键字段
    @JoinTable(name = "t_roles_menus",joinColumns = @JoinColumn(name = "role_id"),inverseJoinColumns = @JoinColumn(name = "menu_id"))
    private Set<Menu> menus=new HashSet<>();
}


public interface MenuRepository extends JpaRepository<Menu,Long> {
    Set<Menu> findMenusByRolesIs(Role role);


}

public interface RoleRepository extends JpaRepository<Role,Long> {
}


public class TestJpa2 {
    @Autowired
    RoleRepository roleRepository;
    @Autowired
    MenuRepository menuRepository;

    @Test
    public void test1(){
        //这里有一些区别,需要我们进行两侧的保存
        //或者在操作上开启级联操作
        Role role=new Role();
        role.setName("role-1");
        Menu menu=new Menu();
        menu.setName("menu-3");
        menu.setUrl("menu-url-3");
        role.setMenus(new HashSet(){{
            add(menu);
        }});
        menu.setRoles(new HashSet(){{
            add(role);
        }});
        this.menuRepository.save(menu);
        //保存后直接获取主键
        System.out.println(menu);
    }

    @Test
    public void test2(){
        //懒加载
        this.roleRepository.findAll().stream().forEach(e->{
            System.out.println(e+" "+this.menuRepository.findMenusByRolesIs(e));
        });
    }

    @Test
    @Transactional
    @Rollback(false)
    public void test3(){
        this.roleRepository.deleteById(1l);
    }

    @Test
    @Transactional
    @Rollback(false)
    public void test4(){
        //应先查询再删除
        this.menuRepository.deleteById(3l);
    }

    @Test
    public void test5(){
        //目标一次性获取信息
//        System.out.println(this.menuRepository.findByIdAndRolesAfter(4l));
    }

等于说时谁@joinColum谁可以去操作对象,Mappred就是维护对象关系

@EntityGraph

这里注意是接口中直接生成的。

猜你喜欢

转载自blog.csdn.net/qq_29164145/article/details/90480205