Skip to content

JPA

项目地址:https://github.com/zhaobao1830/misszb

简介

JPA(Java Persistence API)意即Java持久化API,是Sun官方在JDK5.0后提出的Java持久化规范。主要是为了简化持久层开发以及整合ORM技术,结束Hibernate、TopLink、JDO等ORM框架各自为营的局面。JPA是在吸收现有ORM框架的基础上发展而来,易于使用,伸缩性强。

JPA的优势:标准化、容器级特性的支持、简单方便、查询能力、高级特性。

与Hibernate的关系:

JPA规范本质上就是一种ORM规范,注意不是ORM框架——因为JPA并未提供ORM实现,它只是制订了一些规范,提供了一些编程的API接口,但具体实现则由服务厂商来提供实现。JPA和Hibernate的关系就像JDBC和JDBC驱动的关系,JPA是规范,Hibernate除了作为ORM框架之外,它也是一种JPA实现。

Image text

SpringBoot整合

依赖安装

访问的是mysql数据库

xml
<dependencies>
    <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-jdbc -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>

    <!-- https://mvnrepository.com/artifact/com.mysql/mysql-connector-j -->
    <dependency>
        <groupId>com.mysql</groupId>
        <artifactId>mysql-connector-j</artifactId>
        <version>8.0.32</version>
    </dependency>


    <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-jpa -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
</dependencies>

配置文件

yml
spring:
  datasource:
    url: 数据库路径
    username: 
    password: 
  jpa:
    properties:
      hibernate:
        # 查询的时候在控制台显示jpa生成的sql
        show_sql: true
        # 格式化控制台显示的sql
        format_sql: true

使用

以获取banner信息为例,展示如何使用JPA

一、定义实体类

Banner实体类

java
package com.zb.misszb.model;

import lombok.Getter;
import lombok.Setter;

import javax.persistence.*;
import java.util.List;

@Entity
@Getter
@Setter
@Table(name = "banner")
public class Banner extends BaseEntity {
    @Id
    private Long id;
    private String name;
    private String description;
    private String title;
    private String img;

    @OneToMany(fetch = FetchType.LAZY)
    @JoinColumn(name = "bannerId")
    private List<BannerItem> items;
}

items字段,保存的是BannerItem表的信息

导航关系是一对多,通过@OneToMany(fetch = FetchType.LAZY)注解来定义

fetch = FetchType.LAZY:表示延迟加载

@JoinColumn(name = "bannerId")注解设置逻辑外键为bannerId

BannerItem实体类

java
package com.zb.misszb.model;

import lombok.Getter;
import lombok.Setter;

import javax.persistence.*;

@Entity
@Getter
@Setter
@Table(name = "banner_item")
public class BannerItem extends BaseEntity {
    @Id
    private Long id;
    private String img;
    private String keyword;
    private short type;
    private String name;
    private Long bannerId;
}

二、定义Repository

java
package com.zb.misszb.repository;

import com.zb.misszb.model.Banner;
import org.springframework.data.jpa.repository.JpaRepository;

public interface BannerRepository extends JpaRepository<Banner, Long> {
    Banner findOneById(Long id);

    Banner findOneByName(String name);
}

需要继承JpaRepository接口,泛型传入的值第一个是查询的实体类,第二个是实体类主键的类型

在这里定义操作数据库的方法

三、定义Service和ServiceImpl

BannerService

java
package com.zb.misszb.service;

import com.zb.misszb.model.Banner;

public interface BannerService {
    Banner getBannerByName(String name);
}

BannerServiceImpl

java
package com.zb.misszb.service.impl;

import com.zb.misszb.model.Banner;
import com.zb.misszb.repository.BannerRepository;
import com.zb.misszb.service.BannerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class BannerServiceImpl implements BannerService {
    @Autowired
    private BannerRepository bannerRepository;

    @Override
    public Banner getBannerByName(String name) {
        return bannerRepository.findOneByName(name);
    }
}

第四、定义Controller

BannerController

java
package com.zb.misszb.api.v1;

import com.zb.misszb.exception.http.NotFoundException;
import com.zb.misszb.model.Banner;
import com.zb.misszb.service.BannerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.validation.constraints.NotBlank;

@RestController
@RequestMapping(value = "/banner")
@Validated
public class BannerController {
    @Autowired
    private BannerService bannerService;

    @RequestMapping(value = "/name/{name}", method = RequestMethod.GET)
    public Banner getBannerName(@PathVariable @NotBlank String name) {
        Banner banner = bannerService.getBannerByName(name);
        if (banner == null) {
            throw new NotFoundException(30005);
        }
        return banner;
    }
}

说明

一、一个功能的实现是:Controller => Service => Repository(=>表示调用)

二、@OneToMany(fetch = FetchType.LAZY)

FetchType.LAZY表示延迟加载

我们在查询的时候没有写关于关联表BannerItem的代码,为什么还是能查询出来呢?

这是因为SpringBoot里自带的序列化插件jackson,在序列化的时候,会将实体类里的所有字段进行序列化(没有被@JsonIgnore注解的字段),如果当前字段 关联的其他表,就是去查询关联表

换个说法就是,如果没有序列化关联的字段,那就不会切查询关联表,因为设置了FetchType.LAZY延迟加载

命名方法查询规则

在SpringBoot中由于有JPA,对于简单的查询,是无需写SQL的。JPA提供了命名查询的方式,通过简单的方法命名即可由JPA来解析生成SQL。

  • 1、方法名必须以find、read、get等开头,后面紧跟要查询的实体类名称,例如findByUser或者readByUser

  • 2、可以在实体类名称后面添加一个或多个属性名,以By关键字分割,例如findByUserAndTitle

  • 3、对于属性名,可以使用“属性名”、“属性名关键字属性名”、“属性名_关键字_属性名”等格式进行组合,例如“findByUserName”、“findByUserNameLike”、“findByUserName_Like”

  • 4、可以在属性名后面添加一些关键字,例如“IgnoreCase”、“OrderBy”、“First”、“Top”、“Distinct”等,用于实现不区分大小写、排序、分页、去重等功能

  • 5、如果方法需要传入参数,则在方法名中使用“参数占位符”来表示,例如"findByUserNameAndAgeGreaterThan(String userName, int age)",这里面的“userName”和“age”都是参数占位符

  • 6、参数占位符可以使用“?”或者":参数名"的形式来表示,例如"findByUsernameAndAgeGreaterThan(String userName, int age)"可以写成"findByUserNameAndAgeGreaterThan(String userName, @Param(“age”) int age)"

  • 7、如果方法需要使用原生的SQL查询,则可以使用“@Query”注解,并在其中定义SQL语句,例如“@Query(“select * from user where user_name = ?1”)”

Image text

关联方式

一对一

查询学生和学生信息

第一种方式:使用外键

这里的外键是逻辑外键,通过student里的information_id与information表里的id进行管理

student.java

java
package com.zb.misszb.model;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Getter;
import lombok.Setter;

import javax.persistence.*;
import javax.persistence.criteria.CriteriaBuilder;
import java.util.List;

@Entity
@Getter
@Setter
@Table(name = "student")
public class Student {
    @Id
    @GeneratedValue
    private Long id;
    private String name;
    
    // 一对一
    // 通过student表里定义的information_id外键和information表里的id关联进行查询
    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "informationId", referencedColumnName = "id", insertable = false, updatable = false)
    private Information information;
}

student表

Image text

备注

student类中的@JoinColumn添加insertable = false, updatable = false属性,表明是只读的,不然项目启动会报错

java
package com.zb.misszb.model;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Getter;
import lombok.Setter;

import javax.persistence.*;

@Entity
@Getter
@Setter
@Table(name = "information")
public class Information {
    @Id
    private Long id;
    private String name;
    private Long studentId;
}

第二种方式:使用共享主键

就是把information表中的studentId作为student的外键

student.java

java
package com.zb.misszb.model;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Getter;
import lombok.Setter;

import javax.persistence.*;
import javax.persistence.criteria.CriteriaBuilder;
import java.util.List;

@Entity
@Getter
@Setter
@Table(name = "student")
public class Student {
    @Id
    @GeneratedValue
    private Long id;
    private String name;
    private Long informationId;

    @OneToOne(mappedBy = "student", cascade = CascadeType.ALL)
    @JsonIgnoreProperties("student")
    private Information information;
}

Information.java

java
package com.zb.misszb.model;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Getter;
import lombok.Setter;

import javax.persistence.*;

@Entity
@Getter
@Setter
@Table(name = "information")
public class Information {
    private Long id;
    private String name;
    @Id
    private Long studentId;

    @OneToOne
    @MapsId
    @JoinColumn(name = "studentId")
    @JsonIgnoreProperties("information")
    private Student student;
}

备注

@JsonIgnoreProperties注解可以让查询出的数据不会出现无限嵌套

备注

这种方式我还不理解,先记录下来

第三种方式:建立关联表

数据库新建学生表、学生信息表和关联表三张表

新建Student和Information实体类

Student.java

java
package com.zb.misszb.model;

import lombok.Getter;
import lombok.Setter;

import javax.persistence.*;
@Entity
@Getter
@Setter
@Table(name = "student")
public class Student {
    @Id
    @GeneratedValue
    private Long id;
    private String studentName;

    @OneToOne(cascade = CascadeType.ALL)
    @JoinTable(name = "student_information",
            joinColumns = @JoinColumn(name="student_id"),
            inverseJoinColumns = @JoinColumn(name="information_id")
    )
    private Information information;
}

Information.java

java
package com.zb.misszb.model;

import lombok.Getter;
import lombok.Setter;

import javax.persistence.*;
@Entity
@Getter
@Setter
@Table(name = "information")
public class Information {
    @Id
    @GeneratedValue
    private Long id;
    private String informationName;
}

关联表是student_information

里面的字段是student_id(对应的是student表里的id)和information_id(对应的是information表里的id)

Image text

controller:

java
    @RequestMapping(value = "/getStudent", method = RequestMethod.GET)
    public Student getStudent(@RequestParam Long id) {
        Optional<Student> Student = StudentRepository.findById(id);
        return Student.get();
    }

多对多

项目参考:https://github.com/zhaobao1830/misszb/项目中model/Theme

theme和spu是多对多关系

数据库新建theme表、spu表、them_spu表,theme和spu有实体类

theme.java

java
package com.zb.misszb.model;

import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.Where;

import javax.persistence.*;
import java.util.List;

@Entity
@Getter
@Setter
@Where(clause = "delete_time is null")
public class Theme extends BaseEntity{
    @Id
    private Long id;
    private String title;
    private String description;
    private String name;
    private String extend;
    private String entranceImg;
    private String internalTopImg;
    private Boolean online;
    private String titleImg;
    private String tplName;

    @ManyToMany(fetch = FetchType.LAZY)
    @JoinTable(name="theme_spu", joinColumns = @JoinColumn(name="theme_id")
            , inverseJoinColumns = @JoinColumn(name="spu_id"))
    private List<Spu> spuList;

spu.java

java
package com.zb.misszb.model;

import lombok.Getter;
import lombok.Setter;

import javax.persistence.*;
import java.util.List;

@Entity
@Getter
@Setter
public class Spu extends BaseEntity {
    @Id
    private Long id;
    private String title;
    private String subtitle;
    private Long categoryId;
    private Long rootCategoryId;
    private Boolean online;
    private String price;
    private Long sketchSpecId;
    private Long defaultSkuId;
    private String img;
    private String discountPrice;
    private String description;
    private String tags;
    private Boolean isTest;

    private String forThemeImg;

    @OneToMany(fetch = FetchType.LAZY)
    @JoinColumn(name = "spuId")
    private List<Sku> skuList;

    @OneToMany(fetch = FetchType.LAZY)
    @JoinColumn(name = "spuId")
    private List<SpuImg> spuImgList;

    @OneToMany(fetch = FetchType.LAZY)
    @JoinColumn(name = "spuId")
    private List<SpuDetailImg> spuDetailImgList;
}

theme_spu表:包含them_id和spu_id

Image text

如有转载或 CV 的请标注本站原文地址