Spring Data JPA 使用@OneToOne 介绍

5,407 阅读4分钟

JPA简介:JPA是Java Persistence API的简称,是运行期间实体持久化到数据库(ORM)的一种实现规范,并不是一种实现方式.

当前整合到spring boot中的stater 依赖包中有hibernate的实现(面向sql) , mongodb的实现(面向NoSQL),使用时只需要继承JPA的接口按照JPA的语法规范便可以快速进行持久层(dao 层 repository层)的开发,快速完成持久化,将更多的精力集中于业务逻辑(service层)的开发.

JPA与mybatities不同,包括从底层到使用方式完全不相同. 如果业务中有许多关联关系,比如现在有A,B,C,D,E这几张表,但你真正经常查询的只有A表,其余表都是用来做关联关系的,那我劝你还是乖乖用my batities吧,查询是真的方便,用JPA是真的给自己过不去;如果上述几张表格都经常查询且没什么关联关系,那么骚年,JPA是真的顺手啊!

今天就来简单讲解下使用JPA做表关联查询时@OneToOne的简单使用,后续会继续更新@OneToMany @ManyToOne @ManyToMany的使用

pom关系依赖

 <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.47</version>
        </dependency>

    </dependencies>

实体类:Book.java


import lombok.Getter;
import lombok.Setter;

import javax.persistence.*;
import java.io.Serializable;

/**
 * 书籍与书籍详情为1:1的关系<br>
 * </>要求查询书籍时同步查询出书籍详情<br>
 * </>设计表时,书籍关联到书籍详情的外键<br>
 * </>book类引入detail的唯一标识属性作为外键<br>
 * </>同时,该外键由从表(book表)来维护,实现级联管理
 *
 * @author xiesu / Corbin
 * @version 1.0
 * @date 19-12-20
 */
@Entity(name = "book_info")
@Getter
@Setter
public class Book implements Serializable {
  /** 主键只作为标识,无任何实际意义 */
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Integer id;

  /** 书籍id */
  @Column(name = "book_id", unique = true)
  private Integer bookId;

  /** 书名 */
  @Column(name = "book_name")
  private String name;

  /**
   * 引入外键,在使用@JoinColumn时需要使用,并且此处必须使用insertable = false, updatable = false 作为 <br>
   * </>否则程序运行报错<br>
   * </>
   */
  @Column(name = "detail_id", insertable = false, updatable = false)
  private Integer detailId;

  /**
   * 注解@OneToOne只在一个类中使用即可,关系由引入外键的表来维护 <br>
   * </>此处使用cascade = CascadeType.ALL级联关系,书籍不存在详情没有存在的必要 <br>
   * </>targetEntity表示该表级联的表对应的实体类名
   *
   * <p>注解 @OneToOne 默认为急加载,此处写明,便于理解<br>
   * </>此处不使用mapperBy,使用@JoinColumn,标识自身维护关系<br>
   * </>@JoinColumn 中的name属性表示:该表级联到其他表引入的外键属性(不是类的属性名){@link #detailId}对应的列的列名<br>
   * </>@JoinColumn 中 referencedColumnName 表示,name属性对应的表列名参照的其他表的属性名{@link
   * BookDetail#bookDetailId}对应的表属性的属性名 <br>
   * </>可以实现:插入书籍时同步插入书籍详情,删除书籍时同步删除详情,更新时同步更新
   */
  @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER, targetEntity = BookDetail.class)
  @JoinColumn(name = "detail_id", referencedColumnName = "book_detail_id")
  private BookDetail bookDetail;
}

实体类BookDeatil.java


import lombok.Getter;
import lombok.Setter;

import javax.persistence.*;
import java.io.Serializable;

/**
 * 此表作为主表(被其他表属性作为外键的表)
 *
 * @author xiesu / Corbin
 * @version 1.0
 * @date 19-12-20
 */
@Entity(name = "book_detail")
@Getter
@Setter
public class BookDetail implements Serializable {
  /** 主键只作为标识,无任何实际意义 */
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Integer id;

  /** 唯一标识出详情的id */
  @Column(name = "book_detail_id")
  private Integer bookDetailId;

  /** 书的页数 */
  @Column(name = "book_page")
  private Integer page;

  /**
   * 此处不需要出现被维护方,不能实现双向级联,保存书籍详情时无法同步保存书籍<br>
   * </> 因为book类引入的外键时,设置了insertable = false, updatable = false) <br>
   * </>导致保存书籍详情时,无法插入book的外键<br>
   * </> 因此这里只能单向一对一
   */
  //  /** 被维护方,关系由Book(主表)去维护 */
  //  @OneToOne(mappedBy = "bookDetail", cascade = CascadeType.ALL, fetch =
  // FetchType.EAGER,targetEntity = Book.class)
  //  private Book book;
}

两个repository接口:

import org.corbin.casecadedemo.oneToOne.singletrack.entity.BookDetail;
import org.springframework.data.jpa.repository.JpaRepository;

/**
 * @author xiesu / Corbin
 * @version 1.0
 * @date 19-12-20
 */
public interface BookDetailRepository extends JpaRepository<BookDetail, Integer> {}

--------------------------

import org.corbin.casecadedemo.oneToOne.singletrack.entity.Book;
import org.springframework.data.jpa.repository.JpaRepository;

/**
 * @author xiesu / Corbin
 * @version 1.0
 * @date 19-12-20
 */
public interface BookRepository extends JpaRepository<Book, Integer> {}

注:repository也可以使用注解的形式,例如

/**
 * @author xiesu / Corbin
 * @version 1.0
 * @date 20-1-20
 */
 @RepositoryDefinition(domainClass=Book.class,idClass=Integer.class)
public interface BookRepository{}

这两种方式都是使用jpa接口的正确方式,但使用继承方式往往会更灵活一些, 可以自定义基础接口,继承Jpa的接口,并自定添加一些额外的方法,业务接口再继承自定义的基础接口.

控制器类:


import com.alibaba.fastjson.JSON;
import org.corbin.casecadedemo.oneToOne.singletrack.entity.Book;
import org.corbin.casecadedemo.oneToOne.singletrack.entity.BookDetail;
import org.corbin.casecadedemo.oneToOne.singletrack.repository.BookDetailRepository;
import org.corbin.casecadedemo.oneToOne.singletrack.repository.BookRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author xiesu / Corbin
 * @version 1.0
 * @date 19-12-21
 */
@RestController
@RequestMapping("/")
public class TestController {
  @Autowired private BookRepository bookRepository;
  @Autowired private BookDetailRepository bookDetailRepository;

  @GetMapping("/save-book-with-detail")
  public String saveBookWithDetail() {
    // 书籍
    Book book = new Book();
    book.setBookId(2);
    book.setDetailId(1);
    book.setName("百年孤独");

    // 详情
    BookDetail detail = new BookDetail();
    detail.setBookDetailId(1);
    detail.setPage(100);
    // book 实体设置详情
    book.setBookDetail(detail);

    Book b = bookRepository.save(book);
    return JSON.toJSONString(b);

  }





  @GetMapping("/del-book-with-detail")
  public void delBookWithDetail() {
    bookRepository.deleteById(1);
  }
}

@OneToOne只能是单向的,关系由从表去维护(引入外键的表),可以在保存从表数据时同时保存主表的数据(级联实现),查询,删除时同理. 主表对应的类就不要使用@OneToOne了,因为从表引入的外键必须设置不可插入,不可更新,因此不能在插入主表数据时同步插入从表的数据(从表的外键将会为null).