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).