SpringDataJPA

1 JPA概念

JPA是Java Persistence API的简称,中文名Java持久层API,是JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中.

JPA的总体思想和现有Hibernate、TopLink、JDO等ORM框架大体一致。总的来说,JPA包括以下3方面的技术:

  • ORM映射元数据: JPA支持XML和JDK5.0注解两种元数据的形式,元数据描述对象和表之间的映射关系,框架据此将实体对象持久化到数据库表中.
  • API: 用来操作实体对象,执行CRUD操作,框架在后台替代我们完成所有的事情,开发者从繁琐的JDBC和SQL代码中解脱出来.
  • 查询语言: 这是持久化操作中很重要的一个方面,通过面向对象而非面向数据库的查询语言查询数据,避免程序的SQL语句紧密耦合.

2 SpringDataJPA介绍

Spring Data JPA 是 spring data 项目下的一个模块。提供了一套基于 JPA标准操作数据库的简化方案。底层默认的是依赖 Hibernate JPA 来实现的.

技术特点: 我们只需要定义接口并集成 Spring Data JPA 中所提供的接口就可以了。不需要编写接口实现类.
官网地址:SpringDataJPA

3 SpringDataJPA入门Demo

1 创建工程,导入依赖

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.16.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
         <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>

2 创建application.yml配置文件

server:
  port: 9090
spring:
  application:
    name: spring-data-jpa
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/user?charset=utf-8&serverTimezone=UTC
    username: root
    password: root

  # jpa配置
  jpa:
    show-sql: true
    hibernate:
      # create,创建,不管表之前存不存在,不适合生产环境
      # update,更新,表存在则更新数据,不存在则创建表,适合生产环境
      # none 没有任何操作
      ddl-auto: update

3 创建实体类UserInfo

package com.cf.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.*;

/**
 * 用户实体类
 *
 */
@Data
@Entity //表示当前类是实体类
@Table(name = "tb_user",catalog = "user")//name 表名  catalog数据库名 可省略,因配置文件url中配置了数据库
@AllArgsConstructor//满参构造
@NoArgsConstructor //无参构造
public class UserInfo {

    /**
     * 1 如果数据库表中 的字段名称和实体类中的属性名称保持一致的话,可以不需要加@Column注解
     *
     * 2 @GeneratedValue(strategy=" ")  主键生成策略
     * GenerationType.IDENTITY表示针对于mysql中有自增长的数据的生成策略
     * GenerationType.SEQUENCE表示针对于oracle数据中的主键生成策略
     * GenerationType.AUTO是默认的选项,会根据数据库自动选择
     */
    @Id
    @Column(name = "id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private   Integer id;

    private   String name;
    private   String gender;
    private   Integer age;
    private   String address;
    private   String qq;
    private   String email;
    private   String username;
    private   String password;
    private   String phone;

    public UserInfo(Integer id,String name, String gender) {
        this.id = id;
        this.name = name;
        this.gender = gender;
    }
}

4 创建DAO接口

/**
 * dao接口
 */
@Repository
public interface UserMapper extends JpaRepository<UserInfo,Integer> {
}

5 创建SpringBoot入口启动类和测试类

//启动类
@SpringBootApplication
public class JpaApplication {

    public static void main(String[] args) {
        SpringApplication.run(JpaApplication.class, args);
    }
}
//测试类
@RunWith(SpringRunner.class)
@SpringBootTest
public class JpaTest {

    @Autowired
    private UserMapper userMapper;

    //查询方法
    @Test
    public void queryAll(){
        //查询所有用户
        List<UserInfo> infoList = userMapper.findAll();
        System.out.println(infoList);
    }
}

4 SpringDataJPA的CRUD操作

注:在实体类之上加入有参和无参的构造函数的注解,或自定义参数构造函数

@AllArgsConstructor  #有参构造的注解
@NoArgsConstructor  #无参构造的注解

1 save操作

save操作等于: insert 或 update

/**
     * save操作
     * id在表中存在,则修改数据
     * id不存在,则新增数据
     */
    @Test
    public void save(){

        UserInfo userInfo = new UserInfo(4,"张三","2");
        //新增或修改数据
        UserInfo info = userMapper.save(userInfo);
        System.out.println(info);
        
        UserInfo userInfo2 = new UserInfo(5,"李四","2");
        UserInfo userInfo3 = new UserInfo(6,"王五","2");
        List<UserInfo> list = new ArrayList<>();
        list.add(userInfo2);
        list.add(userInfo3);
        //批量插入数据
        List<UserInfo> userInfos = userMapper.saveAll(list);
        System.out.println(userInfos);
    }

2 delete操作

/**
     * id存在则删除
     * 不存在报错: No class com.cf.pojo.UserInfo entity with id 3 exists!
     */
    @Test
    public void delete(){
        userMapper.deleteById(3);

    }

3 分页查询操作

/**
     * 分页查询
     */
    @Test
    public void pageQuery() {
   
        /**设置分页参数
         * Pageable pageable = PageRequest.of(1, 2)
         * param1:页码,但是默认是从0开始
         * param2:每页显示的记录数
         */
        Pageable pageable = PageRequest.of(1, 2);

        Page<UserInfo> page = userMapper.findAll(pageable);
        //获取分页集合的数据
        List<UserInfo> content = page.getContent();
        System.out.println(content);

        //获取总元素数量
        long totalElements = page.getTotalElements();
        System.out.println(totalElements);

        //获取总页数
        int totalPages = page.getTotalPages();
        System.out.println(totalPages);
    }

5 基于@Query注解查询与更新

1 自定义简单查询

1 在mapper接口中定义接口方法

//自定义查询
    @Query("from UserInfo ")
    public List<UserInfo> selectAll();

2 测试

/**
     * 自定义查询
     */
    @Test
    public void selectAll() {
        List<UserInfo> userInfos = userMapper.selectAll();
        System.out.println(userInfos);

    }

2 自定义简单查询

1 在mapper接口中定义接口方法

//自定义查询带条件
    //@Query注解表示查询,参数的表示使用?1 ,  ?2,    ?n
    @Query("from UserInfo where name=?1 or username=?2")
    public List<UserInfo> selectByExample(String name,String username);

@Query注解表示查询,参数的表示使用?1 , ?2, … ?n
接口方法中的参数的顺序必须要和?后面的数字相对应

2、测试

@Test
    public void selectExample(){
        List<UserInfo> userinfos = userMapper.selectByExample("王五","小小");
        System.out.println(userinfos);
    }

3 自定义更新

1 在mapper接口中定义接口方法

@Repository
@Transactional
public interface UserMapper extends JpaRepository<UserInfo,Integer> {
    @Query("update UserInfo  set name=?1 where id=?2")
    @Modifying
    public int updateUserInfo(String name,Integer id);
}

1 在执行update或者delete的时候,要加上@Modifying表示更新
2 要加入@Transactional注解,因为jpa要求,没有事务支持,不能执行更新和删除操作

2 测试

@Test
    public void update(){
        int i = userMapper.updateUserInfo("abcdefg",2);
        System.out.println(i);
    }

4 常见异常解析

1 错误码:JDBC style parameters (?) are not supported for JPA queries

解决方案: 在HQL的后面加入index(低版本SpringBoot不加是可以运行的)
ps: @Query("update UserInfo set name=?1 where id=?2")

2 错误码:Invalid parameter index! You seem to have declared too little query method parameters!

解决方案: 下标用错了 , 注意 从== 1== 开始 不是 0

3 错误码:org.hibernate.hql.internal.QueryExecutionRequestException: Not supported for DML operations

解决方案: 在方法上加上@Modifying表示更新

4 错误码:Executing an update/delete query; nested exception is javax.persistence.TransactionRequiredException: Executing an update/delete query

解决方案: 在Service层或者Repository层上必须加@Transactional