SpringData Jpa 的介绍

作者: 右耳菌 | 来源:发表于2022-04-30 16:39 被阅读0次

    一、Spring Data 的介绍

    • Spring Data 是什么
      Spring Data是为了简化构建基于Spring框架应用的数据访问技术,包括对关系数据库、非关系数据库、Map-Reduce框架、云数据服务等访问支持。它为我们提供使用统一的API标准来对数据访问层进行操作,这套标准包含了CRUD(创建、获取、更新、删除)、查询、排序和分页的相关操作。
    • Spring Data 官网介绍
      https://spring.io/projects/spring-data
      Spring Data 官网介绍

    二、Spring Data 的特点

    • 支持对象关系映射
      具备ORM框架的对象关系映射功能
    • 统一的Repository接口
    • Repository<T, ID extends Serializable>:统一接口
    • CrudRepository<T, ID extends Serializable>:基本CRUD操作
    • PagingAndSortingRepository<T, ID extendsSerializable>:基本CRUD及分页
    • 统一的数据访问模板类xxxTemplate
      如:MongoTemplateRedisTemplate

    三、JPA 的介绍

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

    四、例子

    1. 流程图


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

    • pom.xml 的依赖配置如下

    重要的配置如下

    • 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>
    
    • application.properties
    • 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
    注意:
    • 若Controller中的findById使用userRepository.getById(1)会抛出 com.fasterxml.jackson.databind.exc.InvalidDefinitionException 异常,解决办法为在对应的实体类(如这里是在User类)中加上 @JsonIgnoreProperties(value = { "hibernateLazyInitializer", "handler" })
    @JsonIgnoreProperties(value = { "hibernateLazyInitializer", "handler" })
    public class User {
    
    • 但是即便如此,在xxxApplicationTests 类中执行userRepository.getById会触发异常 org.hibernate.LazyInitializationException: could not initialize proxy [xxx] - no Session,该问题的分析参考:https://www.baeldung.com/hibernate-initialize-proxy-exception,但是即便是看了也会发现,在这个例子中,我们没办法完美地解决这个问题,一个重要的原因是我们并非来自一个服务器请求(本地测试代码)

    其他情况下解决的方法分析除了上面的链接外还可以参考这个: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,经尝试,无效

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

    相关文章

      网友评论

        本文标题:SpringData Jpa 的介绍

        本文链接:https://www.haomeiwen.com/subject/lfryyrtx.html