右耳菌-邓小白的Java架构师的修炼之路一些收藏

SpringData Jpa 的介绍

2022-04-30  本文已影响0人  右耳菌

一、Spring Data 的介绍

二、Spring Data 的特点

  • Repository<T, ID extends Serializable>:统一接口
  • CrudRepository<T, ID extends Serializable>:基本CRUD操作
  • PagingAndSortingRepository<T, ID extendsSerializable>:基本CRUD及分页

三、JPA 的介绍

  • JPA (Java Persistence API)是Sun官方提出的Java持久化规范。它为Java开发人员提供了一种对象/关联映射工具来管理Java应用中的关系数据。它的出现主要是为了简化现有的持久化开发工作和整合ORM技术,结束现在Hibernate、TopLink、JDO等ORM框架各自为营的局面。
  • 值得注意的是,JPA是在充分吸收了现有的Hibernate、TopLink、JDO等ORM框架的基础上发展而来的,具有易于使用、伸缩性强等优点。
  • JPA是一套规范,不是一套产品,那么像Hibernate、TopLink、JDO它们是一套产品,如果说这些产品实现了这个JPA规范,那么我们就可以称他们为JPA的实现产品。

四、例子

  1. 流程图


    SpringData JPA快速入门流程
  2. 创建SpringBoot项目并且引用相关的依赖

重要的配置如下

  • spring-boot-starter-data-jpa // JPA的依赖
  • spring-boot-starter-web // web的依赖
  • mysql-connector-java // mysql驱动
    <dependencies>
        <!-- JPA的依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <!-- Web的依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- 热部署-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <!-- MySQL驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <!-- Lombok, 简化实体类代码 -->
        <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>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
  • spring.jpa.show-sql=true
    表示支持SQL输出
  • spring.jpa.properties.hibernate.format_sql=true
    表示格式化 SQL 输出
  • spring.jpa.hibernate.ddl-auto=update
    开启数据库表结构自动更新,让表根据Entity类的变化而变化,有多个选项 如 create、create-drop, 生产环境下非必要不要开启
  • spring.jpa.database=mysql
    jpa对应的数据库类型
    properties 的配置如下
# MySql配置
spring.datasource.url=jdbc:mysql://localhost:3306/cloud_study?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true
spring.datasource.username=#### 替换账号
spring.datasource.password=####  替换密码
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

# 支持SQL输出
spring.jpa.show-sql=true
# 格式化 SQL 输出
spring.jpa.properties.hibernate.format_sql=true
# 开启数据库表结构自动更新,让表根据Entity类的变化而变化,有多个选项 如create、create-drop,生产环境下非必要不要开启
spring.jpa.hibernate.ddl-auto=update
# jpa对应的数据库类型
spring.jpa.database=mysql
  1. 配置Datasource


    配置Datasource
  2. 创建实体类User

package cn.lazyfennec.springdatajpademo.entity;

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

import javax.persistence.*;


/**
 * @Author: Neco
 * @Description:
 * @Date: create in 2022/4/30 14:29
 */
@Data
@Entity
@Table(name = "t_user") // 表示对应数据库中的 t_user 表
@AllArgsConstructor
@NoArgsConstructor
public class User {
    @Id
    // 自增ID,可以使用自己的策略
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    // 表示对应的数据库字段为 uname, 不可为空,内容不能重复,长度为20
    @Column(name = "uname", nullable = false, unique = true, length = 20)
    private String username;
    @Column // 不写表示默认
    private String password;

}
  1. 创建UserRepository接口并继承JpaRepository
package cn.lazyfennec.springdatajpademo.reporitory;

import cn.lazyfennec.springdatajpademo.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;

public interface UserRepository extends JpaRepository<User, Integer> {

    // 根据username模糊查询用户,无需编写sql语句,会根据名称自动生成sql语句
    List<User> findUserByUsernameContains(String username);

}
  1. 编写测试代码

package cn.lazyfennec.springdatajpademo;

import cn.lazyfennec.springdatajpademo.entity.User;
import cn.lazyfennec.springdatajpademo.reporitory.UserRepository;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import javax.annotation.Resource;

@SpringBootTest
class SpringDataJpaDemoApplicationTests {

    @Resource
    private UserRepository userRepository;

    @Test
    void contextLoads() {
        userRepository.save(new User(1, "lazyfennec", "123456")); // 保存
        System.out.println(userRepository.findById(1)); // 根据ID查询
        System.out.println(userRepository.findUserByUsernameContains("zy")); // 根据username 模糊查询
    }

}

运行结果如下

// 这里是根据类自动创建表
Hibernate: 
    
    create table t_user (
       id integer not null auto_increment,
        password varchar(255),
        uname varchar(20) not null,
        primary key (id)
    ) engine=InnoDB

Hibernate: 
    
    alter table t_user 
       drop index UK_4cek9ke0uutpr46alkjxaap0s
Hibernate: 
    
    alter table t_user 
       add constraint UK_4cek9ke0uutpr46alkjxaap0s unique (uname)
--------------------------------------------------------------------------
// 这里是保存过程,先查询是否存在,后插入数据
Hibernate: 
    select
        user0_.id as id1_0_0_,
        user0_.password as password2_0_0_,
        user0_.uname as uname3_0_0_ 
    from
        t_user user0_ 
    where
        user0_.id=?
Hibernate: 
    insert 
    into
        t_user
        (password, uname) 
    values
        (?, ?)
Hibernate: 
    select
        user0_.id as id1_0_0_,
        user0_.password as password2_0_0_,
        user0_.uname as uname3_0_0_ 
    from
        t_user user0_ 
    where
        user0_.id=?
Optional[User(id=1, username=lazyfennec, password=123456)]
---------------------------------------------------------------
// 这里是根据username模糊查询
Hibernate: 
    select
        user0_.id as id1_0_,
        user0_.password as password2_0_,
        user0_.uname as uname3_0_ 
    from
        t_user user0_ 
    where
        user0_.uname like ? escape ?
[User(id=1, username=lazyfennec, password=123456)]
  1. 创建UserController
package cn.lazyfennec.springdatajpademo.controller;

import cn.lazyfennec.springdatajpademo.entity.User;
import cn.lazyfennec.springdatajpademo.reporitory.UserRepository;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.List;

/**
 * @Author: Neco
 * @Description:
 * @Date: create in 2022/4/30 14:40
 */
@RestController
public class UserController {
    @Resource
    private UserRepository userRepository;

    /**
     * 根据ID查询
     *
     * @param id
     * @return
     */
    @GetMapping("/user/id/{id}")
    public Object findById(@PathVariable Integer id) {
        return userRepository.findById(id);
    }

    /**
     * 根据Username模糊查询
     *
     * @param username
     * @return
     */
    @GetMapping("/user/username/{username}")
    public List<User> findByUsername(@PathVariable String username) {
        return userRepository.findUserByUsernameContains(username);
    }

    /**
     * 保存
     *
     * @param user
     * @return
     */
    @PostMapping("/user")
    public User saveUser(@RequestBody User user) {
        return userRepository.save(user);
    }

}

测试接口


测试1 测试2 测试3
注意:
@JsonIgnoreProperties(value = { "hibernateLazyInitializer", "handler" })
public class User {

其他情况下解决的方法分析除了上面的链接外还可以参考这个:https://www.cnblogs.com/surging-dandelion/p/15085605.html
大致的意思是:

  1. spring.jpa.open-in-view设置为true可以(其实默认就是true,所以对于解决本文中提到的问题该配置无法解决,设置为false时通过controller访问时,也会触发 org.hibernate.LazyInitializationException: could not initialize proxy [xxx] - no Session),因为在某些情况下会导致死锁的发生,且会降低性能。
  2. spring.jpa.properties.hibernate.enable_lazy_load_no_trans设置为true可以解决本文提到的问题,但是开启它意味着对关联延迟加载实体的每次访问都将包装在新事务中运行的新会话中,简而言之,如果一个用户User有多个角色List<Role> roles的情况下,这意味着用户有一个 SELECT和 N 个额外的 SELECT 来获取每个用户的角色, 我们 最终会遇到一个n+1 的问题
  3. @Proxy(lazy = false) 同样可以本文提到的问题,但是这里是关闭了懒加载,同样会影响性能
  4. @Transactional,经尝试,无效

更多知识,请点击关注查看我的主页信息哦~

上一篇下一篇

猜你喜欢

热点阅读