Spring Boot数据库交互之Spring Data JPA

Spring Data JPA介绍
Spring Boot中支持的数据库交互方式多种多样,今天咱就来玩一下Spring Data JPA好了,因为其他的,咱也还不会呀!?!

JPA全称为Java Persistence API(Java持久层API),它是Sun公司在JavaEE 5中提出的Java持久化规范。它为Java开发人员提供了一种对象/关联映射工具,来管理Java应用中的关系数据,JPA吸取了目前Java持久化技术的优点,旨在规范、简化Java对象的持久化工作。很多ORM框架都是实现了JPA的规范,如:Hibernate、EclipseLink。

Spring Data JPA旨在通过减少实际需要的工作量来显著改善数据访问层的实现。它在JPA的基础上做了一些封装,可以轻松实现基于JPA的存储库。 此模块处理对基于JPA的数据访问层的增强支持。 它使构建使用数据访问技术的Spring驱动应用程序变得更加容易。

需要注意的是JPA统一了Java应用程序访问ORM框架的规范

JPA为我们提供了以下规范:

  1. ORM映射元数据:JPA支持XML和注解两种元数据的形式,元数据描述对象和表之间的映射关系,框架据此将实体对象持久化到数据库表中;
  2. JPA 的API:用来操作实体对象,执行CRUD操作,框架在后台替我们完成所有的事情,开发人员不用再写SQL了(只能说在一定程度上不用写SQL,实际情况可能还是会用一些SQL);
  3. JPQL查询语言:通过面向对象而非面向数据库的查询语言查询数据,避免程序的SQL语句紧密耦合;

以上的定义引用自网络技术文章,我还在不断理解与学习中,我们先来Demo一个例子:

P.S.:本演示是基于上期文章创建的项目演进的:

5分钟入手Spring Boot

集成Spring Data JPA
以连接Oracle为例(网上有很多Mysql的案例,除了配置不一样,其实使用差不多,有机会我们可以Demo Mysql、Mongo的案例)
在项目pom.xml中的dependencies节点加入以下依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>com.oracle</groupId>
    <artifactId>ojdbc8</artifactId>
    <version>12.2.0.1.0</version>
</dependency>

准备数据库配置

  1. 在src/main底下创建resources文件夹;

  2. 在resources文件夹内新建application.properties文件;


    resources
  3. 在application.properties文件内输入数据库的配置信息,如:

spring.datasource.url=jdbc:oracle:thin:@//xxdb-scan:xxxx/XXXX
spring.datasource.username=xxxx
spring.datasource.password=xxxxxxxx
spring.datasource.driver-class-name=oracle.jdbc.OracleDriver
spring.jpa.show-sql=true
spring.jackson.serialization.indent_output=true
  1. 启动项目,验证数据库配置正确与否;


    启动项目 1

    启动项目 2

创建实体类entity
假设我们的数据库中有个表叫LEAD表(也叫lead表),我们想查询LEAD表中的数据;

  1. 创建entity包;
  2. 在entity包内创建实体类Lead.java;


    实体类
  3. 编写实体类;
    (LEAD 表有多个字段,我们本例只使用其中的2个字段,一个是lead_id, 一个是email;)
package com.mycompany.sample.entity;

import javax.persistence.*;

/**
 * @author : dylanz
 * @since : 06/30/2020
 **/
@Entity
@Table(name = "LEAD")
public class Lead {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "lead_id")
    private int leadId;
    @Column(name = "email")
    private String email;

    public Lead() {
    }

    public Lead(Integer leadId, String email) {
        this.leadId = leadId;
        this.email = email;
    }

    public int getLeadId() {
        return leadId;
    }

    public void setLeadId(int leadId) {
        this.leadId = leadId;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
}
#@Table(name = "LEAD")这个非必需,当未提供时,说明本实体类的表名是实体类类名转换后的名字,如:
# 1.实体类名为Lead时,默认表名为:lead表;
# 2.实体类名为UserPermission时,默认表名为:user_permission表;
#@Column(name = "lead_id")中name为非必需,当未提供时,说明数据库的列名为对应成员变量转换后的名字,如:
# 1.当对应的成员变量为private int leadId; 时,默认数据库列名为lead_id;
# 2.当对应的成员变量为private int email; 时,默认数据库列名为email;
#可以看到,实体类和成员变量均为驼峰结构,而对应的表明和数据库列名为下划线结构;
#尽管如此,还是建议补全@Table和@Column;
#Spring Boot还有很多注解和属性,可以用于规范实体类及实体类的列,比如长度限制、数据类型限制等,
#这里不再详细介绍,毕竟我们本例的重点不是介绍这个。

创建repository
有了实体类之后,我们接下来要创建一个repository接口类,并在repository接口类内实现数据库交互,并且把交互结果映射到实体类Lead.java

  1. 创建repository包;
  2. 在repository包内创建repository接口类LeadRepository.java;


    repository接口类
  3. 编写repository接口类;
package com.mycompany.sample.repository;

import com.mycompany.sample.entity.Lead;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;

import javax.transaction.Transactional;

/**
 * @author : dylanz
 * @since : 06/30/2020
 **/
@Repository
public interface LeadRepository extends JpaRepository<Lead, Integer> {
    @Query(value = "select l from Lead l where leadId=?1")
    Lead getLeadByLeadId(Integer leadId);

    @Query(value = "select lead_id,email from lead where lead_id=?1", nativeQuery = true)
    Lead getLeadByLeadIdWithNativeQuery(Integer leadId);

    @Modifying
    @Query(value = "update lead set email=?1 where lead_id=?2", nativeQuery = true)
    @Transactional
    void updateLeadEmail(String email, Integer leadId);
}
#增、删、改的数据库交互,必须搭配@Modifying使用,并且建议也使用注解@Transactional来处理事务,
#即交互失败,Spring Boot会自动帮我们进行回滚。

创建service
repository创建完之后,我们就可以与数据库进行交互了,接下来我们写个service调用repository

  1. 创建service包;
  2. 创建service类;


    service类
  3. 编写service类;
package com.mycompany.sample.service;

import com.mycompany.sample.entity.Lead;
import com.mycompany.sample.repository.LeadRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * @author : dylanz
 * @since : 06/30/2020
 **/
@Service
public class LeadService {
    @Autowired
    private LeadRepository leadRepository;

    public Lead getLead(Integer leadId) {
        return leadRepository.getLeadByLeadId(leadId);
    }

    public Lead getLeadByLeadIdWithNativeQuery(Integer leadId) {
        return leadRepository.getLeadByLeadIdWithNativeQuery(leadId);
    }

    public Lead updateEmail(com.mycompany.sample.domain.Lead lead) {
        int leadId = lead.getLeadId();
        String email = lead.getEmail();
        leadRepository.updateLeadEmail(email, leadId);
        Lead updatedLead = leadRepository.getLeadByLeadId(leadId);

        if (updatedLead.getEmail() != null && !updatedLead.getEmail().equals(email)) {
            throw new InternalError("Unable to update email for leadId: " + leadId + " with email: " + email);
        }
        return updatedLead;
    }
}

开发带数据库交互功能的API
service开发完成后,我们要把该service暴露给客户端使用,于是就要创建API了

  1. 创建Controller;


    Controller
  2. 编写Controller;
package com.mycompany.sample.controller;

import com.mycompany.sample.entity.Lead;
import com.mycompany.sample.service.LeadService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

/**
 * @author : dylanz
 * @since : 06/30/2020
 **/
@RestController
public class LeadController {
    @Autowired
    private LeadService leadService;

    @GetMapping("/getLead/{leadId}")
    @ResponseBody
    public Lead getLead(@PathVariable Integer leadId) {
        return leadService.getLead(leadId);
    }

    @GetMapping("/getLead")
    @ResponseBody
    public Lead getLeadById(@RequestParam("leadId") Integer leadId) {
        return leadService.getLeadByLeadIdWithNativeQuery(leadId);
    }

    @PutMapping(value = "/updateLeadEmail")
    @ResponseBody
    public Lead updateLeadEmail(@RequestBody com.mycompany.sample.domain.Lead lead) {
        return leadService.updateEmail(lead);
    }
}
  1. 为了演示PUT请求,我还建了个updateLeadEmail API,并且引入lombok;
  • pom.xml中dependencies节点内添加lombok依赖
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.12</version>
    <scope>provided</scope>
</dependency>
  • 建立domain包;
  • 编写Lead domain;
package com.mycompany.sample.domain;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;

import java.io.Serializable;

/**
 * @author : dylanz
 * @since : 06/30/2020
 **/
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class Lead implements Serializable {
    private static final long serialVersionUID = 1L;

    private Integer leadId;
    private String email;
}

运行Spring Boot项目,见证奇迹

调用Get API,返回执行结果:

http://127.0.0.1:8080/getLead?leadId=10xxxx46http://127.0.0.1:8080/getLead/10xxxx46

API交互结果 1

API交互结果 2

调用Put API,返回执行结果:

API交互结果 3
  1. 同时项目log中,我们可以找到被执行的sql:
    为了能看到sql,关键一步是src/main/resources/application.properties内的配置项:spring.jpa.show-sql=true
    后台log

其中第一条SQL是非nativeQuery的,第二、三条SQL是nativeQuery的,nativeQuery的SQL就是我们平常写的sql,而非nativeQuery的SQL,是Spring Boot JPA帮我们生成的。

到此为止,我们已经完成了Spring Boot项目中采用Spring Boot JPA方式与数据库交互的实现!

码字不容易,点赞需积极

谢谢!!!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349