SpringBoot --- 整合Spring Data JPA

SpringBoot --- 整合Spring Data JPA

    • 1.介绍
    • 2.依赖
    • 3.配置文件
    • 4.实体类
    • 5.Repository
    • 6.CrudRepository
    • 7.PagingAndSortingRepository
    • 8.JpaRepository
    • 9.JPASpecificationExecutor接口
    • 10.关联关系查询
      • 10.1 注解介绍
      • 10.2 一对一
      • 10.3 一对多
      • 10.4 多对多
    • 11.级联操作

整理不易,不喜勿喷。谢谢

SpringBoot — 整合Ldap.
SpringBoot — 整合Spring Data JPA.
SpringBoot — 整合Elasticsearch.
SpringBoot — 整合spring-data-jpa和spring-data-elasticsearch.
SpringBoot — 整合thymeleaf.
SpringBoot — 注入第三方jar包.
SpringBoot — 整合Redis.
Springboot — 整合slf4j打印日志.
Springboot — 整合定时任务,自动执行方法.
Springboot — 配置多数据源,使用JdbcTemplate以及NamedParameterJdbcTemplate.
Sprignboot — 详解pom.xml中build和profile.
SpringBoot — 监控.
SpringBoot — 缓存Cache/Redis.
SpringBoot与Zookeeper.
Git的使用.

1.介绍

Repository:是Spring Data的核心概念,抽象了对数据库和Nosql的操作

接口如下

  • Repository
  • CrudRepository 提供了基础的增删改查,批量操作接口
  • JpaRepository 提供了更丰富的数据库访问接口
  • PagingAndSortingReposlitory 集成CrudRepository,附加了分页查询功能
  • JPASpecificationExecutor接口

SpringBoot --- 整合Spring Data JPA_第1张图片

2.依赖

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </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>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>com.oracle</groupId>
            <artifactId>ojdbc6</artifactId>
            <version>11.2.0.3</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

    </dependencies>

3.配置文件

server.port=8083

spring.datasource.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver
spring.datasource.jdbc-url=jdbc:oracle:thin:@//10.xxx.14:1521/Pxxxxx
spring.datasource.username=study123456
spring.datasource.password=study123456

spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

import javax.sql.DataSource;

@Configuration
public class DataSourceConfig {

    @Bean(name = "dataSource1")
    @Qualifier("dataSource1")
    @Primary
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource dataSource1(){
        return DataSourceBuilder.create().build();
    }
}

4.实体类

GenerationType四中类型:

  • TABLE:使用一个特定的数据库表格来保存主键。
  • SEQUENCE:根据底层数据库的序列来生成主键,条件是数据库支持序列。
  • IDENTITY:主键由数据库自动生成(主要是自动增长型)
  • AUTO:主键由程序控制。
@Data
@Entity
@Table(name = "USERS")
public class Users {

  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  //@GeneratedValue(strategy = GenerationType.IDENTITY)    主键自增
  @Column(name = "id")
  private int id;

  @Column
  private String username;

  @Column
  private String password;

  @Column
  private int age;
}

5.Repository

要严格遵守命名规则,findBy + 某个字段

常用关键字如下:
-and
And 例如:findByUsernameAndPassword(String user, Striang pwd);

-or
Or 例如:findByUsernameOrAddress(String user, String addr);

-between
Between 例如:SalaryBetween(int max, int min);

-"<"
LessThan 例如: findBySalaryLessThan(int max);

-">"
GreaterThan 例如: findBySalaryGreaterThan(int min);

-is null
IsNull 例如: findByUsernameIsNull();

-is not null
IsNotNull NotNull 与 IsNotNull 等价 例如: findByUsernameIsNotNull();

-like
Like 例如: findByUsernameLike(String user);

-not like
NotLike 例如: findByUsernameNotLike(String user);

-order by
OrderBy 例如: findByUsernameOrderByNameAsc(String user);直接通过name正序排序

-"!="
Not 例如: findByUsernameNot(String user);

-in
In 例如: findByUsernameIn(Collection userList) ,方法的参数可以是 Collection 类型,也可以是数组或者不定长参数;

-not in
NotIn 例如: findByUsernameNotIn(Collection userList) ,方法的参数可以是 Collection 类型,也可以是数组或者不定长参数;

-Top/Limit
查询方法结果的数量可以通过关键字来限制,first 或者 top都可以使用。top/first加数字可以指定要返回最大结果的大小 默认为1
import org.springframework.data.repository.Repository;
import org.springframework.stereotype.Component;

import java.util.List;

@Component
public interface UserRepository extends Repository<Users,Integer> {
	//方法名称必须要遵循驼峰式命名规则,findBy(关键字)+属性名称(首字母大写)+查询条件(首字母大写)
    List<Users> findByUsername(String name);

    List<Users> findByUsernameAndAge(String name,int age);

    List<Users> findByUsernameLike(String name);

}

6.CrudRepository

import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Component;

@Component
public interface UserRepository extends CrudRepository<Users,Integer> {

}
    @Autowired
    UserRepository userRepository;

    @Test
    void test4() {
        Users users = new Users();
        users.setId(5);
        users.setUsername("leon");
        users.setPassword("123");
        users.setAge(18);
        userRepository.save(users);
    }

    @Test
    void test5() {
        Iterable<Users> all = userRepository.findAll();
        for (Users users : all) {
            System.out.println(users);
        }
    }

    @Test
    void test6() {
        Optional<Users> byId = userRepository.findById(1);
        System.out.println(byId);

    }

CrudRepository的方法如下

     S save(S var1);

     Iterable saveAll(Iterable var1);

    Optional findById(ID var1);

    boolean existsById(ID var1);

    Iterable findAll();

    Iterable findAllById(Iterable var1);

    long count();

    void deleteById(ID var1);

    void delete(T var1);

    void deleteAll(Iterable var1);

    void deleteAll();

7.PagingAndSortingRepository

import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.stereotype.Component;

import java.util.List;

@Component
public interface UserRepository extends PagingAndSortingRepository<Users,Integer> {

}

注意:API变更
Sort sort=Sort.by(new Sort.Order(Sort.Direction.DESC,“id”));
Pageable pageable = PageRequest.of(int page, int size, Sort sort);

    @Autowired
    UserRepository userRepository;

    @Test
    void test() {
        //Pageable:封装了分页的参数,当前页,煤业显示的条数。注意:它的当前页是从0开始
        //PageRequest(page,size):page表示当前页,size表示每页显示多少条

        Sort sort=Sort.by(new Sort.Order(Sort.Direction.DESC,"id"));
        Pageable pageable=PageRequest.of(1,2,sort);
        Page<Users> all = userRepository.findAll(pageable);
        System.out.println("数据的总条数:"+all.getTotalElements());
        System.out.println("总页数:"+all.getTotalPages());
        List<Users> list=all.getContent();
        for (Users users:list){
            System.out.println(users);
        }
        
    }

8.JpaRepository

  • 两种传参的方式
  • select只能选一个字段
  • update、delete要加@Modifying,并且方法加上事务@Transictional
import com.example.jpa.entity.Users;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Optional;

@Component
public interface UserRepository extends JpaRepository<Users,Integer> {

    /**
     * 可以重写
     */
    @Override
    Optional<Users> findById(Integer integer);

    /**
     * 对于复杂查询,可以自己写sql语句。自己写sql语句时,方法名不需要遵守规范
     * 但表名要换成实体类名,字段名要换成员变量名
     */
    @Query("select user from Users user where user.username=?1 and user.password=?2")
    List<Users> findAllByUsernameAndPassword(String username, String password);


    @Query("select user from Users user where user.username=:username and user.password=:password")
    List<Users> findAllByUsernameAndPassword2(@Param("username") String username, @Param("password") String password);


    /**
     * 可以select user.xxx选中一个字段,方法返回写该字段的类型或者该类型的List
     * 也可以select user选中全部字段,方法返回写实体类或者实体类的List
     * 要取多个字段时,直接select user取全部字段
     * 也不能select user.*使用*号
     */
    @Query("select user.age from Users user where user.username=:username and user.password=:password")
    List<String> findAllByUsernameAndPassword3(@Param("username") String username, @Param("password") String password);


    /**
     * 写update、delete语句时,需要加 @Modifying
     * 调用时不管调用几个dao层的方法,只要调用了update|delete,都必须要在方法上加事务 @Transactional
     * 加spring的事务、javax的事务均可
     */
    @Modifying
    @Query("update Users user set user.username=:username,user.password=:password where user.id=:id")
    int updateUsernameAndPassword(@Param("id") Integer id, @Param("username") String username, @Param("password") String password);

}
//源码方法如下:
@NoRepositoryBean
public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
    List<T> findAll();

    List<T> findAll(Sort var1);

    List<T> findAllById(Iterable<ID> var1);

    <S extends T> List<S> saveAll(Iterable<S> var1);

    void flush();

    <S extends T> S saveAndFlush(S var1);

    void deleteInBatch(Iterable<T> var1);

    void deleteAllInBatch();

    T getOne(ID var1);

    <S extends T> List<S> findAll(Example<S> var1);

    <S extends T> List<S> findAll(Example<S> var1, Sort var2);
}

9.JPASpecificationExecutor接口

Spring Data JPA支持Criteria查询,可以很方便地使用,足以应付工作中的所有复杂查询的情况
使用Specification的要点就是CriteriaBuilder

package org.springframework.data.jpa.repository;

import java.util.List;
import java.util.Optional;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.lang.Nullable;

public interface JpaSpecificationExecutor<T> {

	Optional<T> findOne(@Nullable Specification<T> spec);

	List<T> findAll(@Nullable Specification<T> spec);

	Page<T> findAll(@Nullable Specification<T> spec, Pageable pageable);

	List<T> findAll(@Nullable Specification<T> spec, Sort sort);

	long count(@Nullable Specification<T> spec);
}

Demo:简单查询

    @Autowired
    StudentSpecificationRepository studentSpecificationRepository;

    @Test
    void test() {
    List<Student> stus = studentSpecificationRepository.findAll(new Specification<Student>() {
           @Override
           public Predicate toPredicate(Root<Student> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
               //root.get("address")表示获取address这个字段名称,like表示执行like查询,%zt%表示值
               Predicate p1 = criteriaBuilder.like(root.get("address"), "%zt%");
               Predicate p2 = criteriaBuilder.greaterThan(root.get("id"),3);
               //将两个查询条件联合起来之后返回Predicate对象
               return criteriaBuilder.and(p1,p2);
           }
       });
       Assert.assertEquals(2,stus.size());
       Assert.assertEquals("oo",stus.get(0).getName());
   }

Demo1:多个Specification

    @Autowired
    StudentSpecificationRepository studentSpecificationRepository;

    @Test
    void test() {
	//第一个Specification定义了两个or的组合
	Specification<Student> s1 = new Specification<Student>() {
    @Override
    public Predicate toPredicate(Root<Student> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
        Predicate p1 = criteriaBuilder.equal(root.get("id"),"2");
        Predicate p2 = criteriaBuilder.equal(root.get("id"),"3");
        return criteriaBuilder.or(p1,p2);
    }
	};
//第二个Specification定义了两个or的组合
	Specification<Student> s2 = new Specification<Student>() {
    @Override
    public Predicate toPredicate(Root<Student> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
        Predicate p1 = criteriaBuilder.like(root.get("address"),"zt%");
        Predicate p2 = criteriaBuilder.like(root.get("name"),"foo%");
        return criteriaBuilder.or(p1,p2);
    }
	};
//通过Specifications将两个Specification连接起来,第一个条件加where,第二个是and
	List<Student> stus = studentSpecificationRepository.findAll(Specifications.where(s1).and(s2));

    Assert.assertEquals(1,stus.size());
    Assert.assertEquals(3,stus.get(0).getId());
}

10.关联关系查询

10.1 注解介绍

@ManyToMany
	作用:用于映射多对多关系
	属性:
		cascade:配置级联操作。
		fetch:配置是否采用延迟加载。
    	targetEntity:配置目标的实体类。映射多对多的时候不用写。

@JoinTable
    作用:针对中间表的配置
    属性:
    	nam:配置中间表的名称
    	joinColumns:中间表的外键字段关联当前实体类所对应表的主键字段			  			
    	inverseJoinColumn:中间表的外键字段关联对方表的主键字段
    	
@JoinColumn
    作用:用于定义主键字段和外键字段的对应关系。
    属性:
    	name:指定外键字段的名称
    	referencedColumnName:指定关联表的主键字段名称
    	unique:是否唯一。默认值不唯一
    	nullable:是否允许为空。默认值允许。
    	insertable:是否允许插入。默认值允许。
    	updatable:是否允许更新。默认值允许。
    	columnDefinition:列的定义信息。

10.2 一对一

@Entity
@Data
public class User implements Serializable {

    @Id
    @GeneratedValue
    private Integer id;

    private String username;

    private String password;

    private String tel;

    private String address;

    @OneToOne
    @JoinColumn(name = "card_id")  //指定该实体类对应的外键
    private Card card;
    
}
@Data
@Entity
public class Card implements Serializable {

    @Id
    @GeneratedValue
    private Integer id;

    private BigDecimal money;

    @OneToOne
    @JoinColumn(name = "user_id")  //此处可不要@JoinColumn
    @JsonIgnore  //放弃维护此字段
    private User user;

}

10.3 一对多

员工employee和部门deptment

  • JPA使用@OneToMany和@ManyToOne来标识一对多的双向关联。
  • 一端(Department )使用 @OneToMany 。
  • 多端(Employee )使用 @ManyToOne。
  • 在JPA规范中,一对多的双向关系由多端(Employee )来维护。就是说多端(Employee )为关系维护端,负责关系的增删改查。一端(Department )则为关系被维护端,不能维护关系。
  • 一端(Department) 使用 @OneToMany 注释的mappedBy="xxxxx"属性表明Department 是关系被维护端。
@Entity
@Data
public class User implements Serializable {

    @Id
    @GeneratedValue
    private Integer id;

    private String username;

    private String password;

    private String tel;

    private String address;

    @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)  //mappedBy=当前实体类对应的表
    private List<Order> orderList;

}
@Data
@Entity
@Table(name = "tb_order")  //order和sql的排序关键字order冲突,不能使用order作为表名
public class Order implements Serializable {

    @Id
    @GeneratedValue
    private Integer id;

    @ManyToOne(targetEntity = User.class, fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id")  //在多的一方指定外键
    @JsonIgnore  //放弃维护此字段
    private User user;

}

10.4 多对多

@Data
@Entity
@Table(name = "sys_user")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "user_id")
    private Long userId;
    @Column(name = "user_name")
    private String userName;
    @Column(name="age")
    private Integer age;


    /**
     * 配置用户到角色的多对多
     *   配置多对多的映射关系
     *       1.声明表关系的配置
     *       @ManyToMany(targetEntity = Role.class)//声明多对多
     *       targetEntity:代表对方实体类字节码
     *       2.配置中间表(包含两个外键)
     *       @JoinTable
     *          name:中间表的名称
     *          joinColumns:配置当前对象在中间表的外键
     *               接收@inverseJoinColumns数组   name外键名  referencedColumnName:参照的主表主键名
     *          inverseJoinColumns:配置对方对象在中间表的外键
     *               接收@inverseJoinColumns数组   name外键名  referencedColumnName:参照的主表主键名
     * @return
     */
    @ManyToMany(targetEntity = Role.class)
    @JoinTable(name = "sys_user_role",
            //joinColumns:当前对象在中间表中的外键  referencedColumnName 来源
            joinColumns = {@JoinColumn(name = "sys_user_id",referencedColumnName = "user_id")},
            //inverseJoinColumns:对方对象在中间表中的外键
            inverseJoinColumns ={@JoinColumn(name = "sys_role_id",referencedColumnName = "role_id")} )
    private Set<Role> roles=new HashSet<Role>();
}

在多对多(保存)中,如果双向都设置关系,意味着双方都维护中间表,都会往中间表插入数据,中间表的2个字段又作为联合主键,所以报错,主键重复,解决保存失败的问题:只需要在任意一方放弃对中间表的维护权即可,推荐在被动的一方放弃

@Data
@Entity
@Table(name ="sys_role")
public class Role {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "role_id")
    private Long roleId;
    @Column(name = "role_name")
    private String roleName;

   //配置多对多  放弃维护
    @ManyToMany(mappedBy = "roles")//配置
   private Set<User> users=new HashSet<User>();
}

11.级联操作

//cascade:配置级联操作   All级联所有操作
@OneToMany(targetEntity = Role.class,cascade = CascadeType.ALL)

注意: cascade = CascadeType.ALL 只能写在 One 端,只有One端改变Many端,不准Many端改变One端。
比如 文章和评论,文章是One,评论是Many,如果删除一条评论,就把文章删了。
    @Test
    @Transactional
    @Rollback(value = false)
    public void testCasCadeAdd(){
        User user=new User();
        user.setUserName("小张");

        Role role=new Role();
        role.setRoleName("程序猿");

        user.getRoles().add(role);//配置用户到角色关系,可以对中间表中的数据进行维护

        role.getUsers().add(user);//配置角色到用户的关系,可以对中间表的数据进行维护

        userDao.save(user);
        
        //查询一号用户
        User one = userDao.findOne(1l);
        //删除一号用户
        userDao.delete(one);

    }
    @Test
    @Transactional  //解决java代码中的no session问题
    @Rollback(value = false)
    public void query() {
        //查询id为一的客户
        Customer customer = customerDao.getOne(1l);
        //对象导航查询,此客户下的所有联系人
        Set<LinkMan> linkMans = customer.getLinkMans();
        for (LinkMan linkMan : linkMans) {
            System.out.println(linkMan);
        }
    }

你可能感兴趣的:(Springboot,Java,spring,boot)