第6章 数据持久化 Spring Data JPA

6.1、本节目标:

  • JPA 简介
  • Spring Data JPA 用法介绍
  • Spring Data JPA、Hibernate/Mybatis/Mybatis-plus 与 Spring Boot 集成
  • 数据持久化实战

6.2、JPA 简介

6.2.1、什么是JPA?

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

  • 最新规范为 JSR 338: JavaTM Persistence 2.2

  • 实现: 支持 JPA 2.2 版本的框架

6.2.2、JPA 核心概念

  • 实体

    • 实体表示关系数据库中的表
    • 每个实体实例对应于该表中的行
    • 类必须用 javax.persistence.Entity 注解
    • 类必须有一个 publicprotected 的无参构造函数
    • 实体实例被当做值以分离对象方式进行传递(例如通过会话bean的远程业务接口),则该类必须实现 Serializable 接口
    • 唯一的对象识别符:简单主键(javax.persistence.idjavax.persistence.Id)、复合主键(javax.persistence.EmbeddedIdjavax.persistence.IdClass
  • 关系

    • 一对一:@OneToOne
    • 一对多:@OneToMany
    • 多对一:@ManyToOne
    • 多对多:@ManyToMany

6.2.3、EntityManager

EntityManager接口:

  • 定义用于与持久性上下文进项交互的方法
  • 创建和删除持久实体实例,通过实体的主键查找实体
  • 允许在实体上运行查询

获取 EntityManager 实例:

EntityManagerFactory emf=Persistence.createEntityManagerFactory("Student_details");  

查找实体:

EntityManagerFactory emf=Persistence.createEntityManagerFactory("Student_details");  
EntityManager em=emf.createEntityManager();  
StudentEntity s=em.find(StudentEntity.class,101); 

6.3、Spring Data JPA

6.3.1、什么是 Spring Data JPA?

  • 是较大的 Spring Data 家族的一部分
  • 对基于 JPA 的数据访问层的增强支持
  • 使得构建使用数据访问技术的 spring 驱动的应用程序更加容易
  • 官方文档

6.3.2、常用接口

CrudRepository:

@NoRepositoryBean
public interface CrudRepository<T, ID> extends Repository<T, ID> {
    <S extends T> S save(S var1);

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

    Optional<T> findById(ID var1);

    boolean existsById(ID var1);

    Iterable<T> findAll();

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

    long count();

    void deleteById(ID var1);

    void delete(T var1);

    void deleteAll(Iterable<? extends T> var1);

    void deleteAll();
}

PagingAndSortingRepository用来分页:

public interface PagingAndSortingRepository<T, ID> extends CrudRepository<T, ID> {
    Iterable<T> findAll(Sort var1);

    Page<T> findAll(Pageable var1);
}

自定义查询:根据方法名创建查询,继承Repository

6.4、Spring Data JPA、Hibernate 与 Spring Boot 集成

环境准备

  • Mysql 8
  • Spring Data JPA
  • Hibernate
  • Mysql Connector/J

新建一个 jpa-in-action 的项目,将上一节中thymeleaf-in-action 中的内容拷贝过来。

修改pom文件,添加相关依赖:

<!-- spring-boot-starter-data-jpa依赖,可以省略版本,Spring Boot中已已自动帮我们定义依赖版本 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- mysql依赖 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.16</version>
</dependency>
<!-- 内存数据库,只在运行时使用,因为目前还没有使用mysql数据库,引入了mysql的依赖之后启动会报错,所以先引入h2内存数据库-->
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <version>1.4.199</version>
    <scope>runtime</scope>
</dependency>

注意:上面引入了H2内存数据库,在运行时使用,如果不引入,因为我们目前还没有指定数据源,但是又引入了mysql的依赖,直接启动项目回报如下错误:

Action:

Consider the following:
If you want an embedded database (H2, HSQL or Derby), please put it on the classpath.
If you have database settings to be loaded from a particular profile you may need to activate it (no profiles are currently active).

修改我们的实体类User

@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
public class User implements Serializable {
    /** 用户的唯一标识 */
    @Id
    private Long id;
    private String name;
    private Integer age;
}

注意:我们要在实体类上添加@Entity@Id注解,同时要实现Serializable,不然我们之后在浏览器端进行用户创建操作的时候无法在h2中自动创建表。

删除原有的 impl 包及其包中的内容,修改 UserRepository,使其继承JPA的CrudRepository接口:

public interface UserRepository extends CrudRepository<User, Long> {

}

同时我们还要修改UserController,使其使用CrudRepository提供的默认方法:

@RestController
@RequestMapping("/users")
public class UserController {

    @Autowired
    private UserRepository userRepository;

    /**
     * 从 用户存储库 获取用户列表
     *
     * @return
     */
    private List<User> getUserList() {
        List<User> list = new ArrayList<>();
        Iterable<User> users = userRepository.findAll();
        users.forEach(user -> {
            list.add(user);
        });
        return list;
    }

    /**
     * 查询所用用户
     *
     * @param model
     * @return
     */
    @GetMapping
    public ModelAndView list(Model model) {
        model.addAttribute("userList", this.getUserList());
        model.addAttribute("title", "用户管理");
        return new ModelAndView("users/list", "userModel", model);
    }

    /**
     * 根据id查询用户
     *
     * @param id
     * @param model
     * @return
     */
    @GetMapping("{id}")
    public ModelAndView view(@PathVariable("id") Long id, Model model) {
        Optional<User> user = userRepository.findById(id);
        model.addAttribute("user", user);
        model.addAttribute("title", "查看用户");
        return new ModelAndView("users/view", "userModel", model);
    }

    /**
     * 获取 form 表单页面
     *
     * @param model
     * @return
     */
    @GetMapping("/form")
    public ModelAndView createForm(Model model) {
        model.addAttribute("user", new User());
        model.addAttribute("title", "创建用户");
        return new ModelAndView("users/form", "userModel", model);
    }

    /**
     * 新建用户
     *
     * @param user
     * @return
     */
    @PostMapping
    public ModelAndView create(User user) {
        user = userRepository.save(user);
        return new ModelAndView("redirect:/users");
    }

    /**
     * 删除用户
     *
     * @param id
     * @param model
     * @return
     */
    @GetMapping(value = "delete/{id}")
    public ModelAndView delete(@PathVariable("id") Long id, Model model) {
        userRepository.deleteById(id);
        model.addAttribute("userList", this.getUserList());
        model.addAttribute("title", "删除用户");
        return new ModelAndView("users/list", "userModel", model);
    }

    /**
     * 修改用户
     *
     * @param id
     * @param model
     * @return
     */
    @GetMapping(value = "modify/{id}")
    public ModelAndView modifyForm(@PathVariable("id") Long id, Model model) {
        Optional<User> user = userRepository.findById(id);
        model.addAttribute("user", user);
        model.addAttribute("title", "修改用户");
        return new ModelAndView("users/form", "userModel", model);
    }

}

同时我们在application.properties文件中添加h2的web访问配置,这样我们就可以在浏览器端访问内存数据库:

# 使用 h2 的控制台,便于我们在web端访问内存数据库,
spring.h2.console.enabled=true

启动我们的应用,访问 http://localhost:8080/users 创建几条数据,然后在浏览器访问 http://localhost:8080/h2-console ,可以看到h2的控制界面:

image-20191127215227122

默认是没有密码的,我们直接点击 Connect 就可以连接上。 注意:JDBC URL默认是jdbc:h2:~/test,而Spring Boot的默认数据库url应该是jdbc:h2:mem:testdb,否则进去后找不到JPA创建的数据表,如果连接报错,一般是JDBC URL错误,我们可以在启动日志中找到我们的内存数据库的地址:

image-20191127215126139

我们点击 Connect 后可以看到下面的界面:

image-20191127230546624

6.5、数据持久化实战

我们接着上一节的项目来学习,在6.4节我们将数据保存到了内存数据库h2中。但是在实际的生产环境中,我们是不可能将数据保存在内存中的,而是持久化到Mysql等数据库中。这一节我们就介绍如何将数据保存到mysql中。

修改application.properties,添加数据源:

# DataSource
spring.datasource.url=jdbc:mysql://localhost/blog?characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

# JPA 打印sql
spring.jpa.show-sql = true
# 在应用每次启动时删除数据库重新创建
spring.jpa.hibernate.ddl-auto=create-drop

安装启动 Mysql Server

安装mysql可以选择在本地安装,也可以选择安装在linux虚拟机中,或者在虚拟机中安装docker,使用docker容器安装都可以,这里不过多叙述,读者请自行安装准备好。

Mysql 数据库中创建blog数据库:

CREATE DATABASE blog;

启动项目,我们可以看到在idea的控制台打印了如下信息:

Hibernate: drop table if exists user
Hibernate: create table user (id bigint not null auto_increment, age integer, name varchar(255), primary key (id)) engine=InnoDB

打开数据库管理器可以看到JPA自动帮我们在数据库中创建了数据表:

image-20191128230029112

我们在浏览器端访问我们的项目界面,插入数据,可以看到用户信息被持久化到了mysql中。而我们访问 http://localhost:8080/h2-console 查看我们的内存数据库,发现内存数据库是无法进入的,因为这个时候我们指定了数据源为mysql数据库,因此内存数据库中就不会存在数据库表,我们在控制台也可以看到如下信息:

H2 console available at '/h2-console'. Database available at 'jdbc:mysql://localhost/blog?characterEncoding=utf-8&useSSL=false&serverTimezone=UTC

以上,就是我们有关Spring Data JPA 的简单介绍,更多内容我们在之后的使用中还会讲到。更对内容可以查看源代码获取。

源代码

jpa-in-action

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值