前言
使用过Hibernate的同学应该知道,Hibernate的作用就是面向对象写SQL,Hibernate要求我们尽量关注与对象之间的关系,封装底层的数据库,用对象之间的映射关系去建立表之间的联系。
而JPA与Hibernate十分相似,也是可以利用对象来直接操作数据库。它只需要极其简单的几个类就可以完成对一个对象的CURD操作。具体如何简单,且看接下来的步骤。
引入JPA依赖
构建好SpringBoot项目之后,引入JPA以及mysql-connector依赖。
<!-- JPA -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- mysql-connector -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
配置数据源及JPA
接下来就需要配置好数据库的连接以及JPA的各项配置
# 数据源配置
spring.datasource.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=mysql
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# JPA配置
# 配置方言,即操作哪种数据库,支持MySql,Oracle等
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
# 配置是否显示sql语句,调试时可以配置为true
spring.jpa.show-sql=true
# 配置执行模式,可选create、update
spring.jpa.hibernate.ddl-auto=create
此处JPA像Hibernate配置一样,需要配置方言,也可以配置是否显示sql语句
创建实体类
创建一个我们常用的用户类,User
package com.demo.jpademo.entity;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
/**
* @author chenxiaotao
* @version 1.0
* @date 2019/3/25 15:52
**/
@Entity
@Table(name = "USER")
public class User {
@Id()
private Long id;
@Column(length = 32)
private String name;
@Column(length = 16)
private String account;
@Column(length = 32)
private String pwd;
}
部分注解解释:
@Entity:注解该类为一个实体类
@Table:标识所对应的数据表名
@Id:标识该属性为主键
@Column:标识该属性为字段,并可以指明字段的长度
到这里,我们可以说已经创建好了一个表,你可以尝试运行一下SpringBoot,可以在控制台看到这样两条输出语句
Hibernate: drop table if exists user
Hibernate: create table auth_user (id bigint not null, account varchar(16), name varchar(32), pwd varchar(32), primary key (id)) engine=InnoDB
此处的 drop table if exists user 语句,是因为我们之前的 spring.jpa.hibernate.ddl-auto 配置项配置为了 create
可以用navicat看到,JPA帮我们创建了一张user表,节省了我们先去创建表后创建实体类这样一个过程,接下来更加强大的是JPA的快速构建CURD功能
创建DAO
创建一个UserDAO接口并继承 JpaRepository<Entity, ID> 接口,这里两个泛型,第一个表示的是实体类,第二个表示的实体类的ID字段类型
package com.demo.jpademo.dao;
import com.demo.jpademo.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
/**
* @author chenxiaotao
* @version 1.0
* @date 2019/3/25 16:02
**/
@Repository
public interface UserDao extends JpaRepository<User, Long> {
}
好了,到这里一个具有CURD的DAO类就已经创建好了。并没有在开玩笑,不信的话,我们来进行测试。
测试DAO
新建一个测试类 UserDOTest
package com.demo.jpademo;
import com.demo.jpademo.dao.UserDao;
import com.demo.jpademo.entity.User;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
/**
* @author chenxiaotao
* @version 1.0
* @date 2019/3/25 16:04
**/
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserDOTest {
@Autowired
private UserDao userDao;
@Before
public void before() {
User userDO = new User();
userDO.setId(5L);
userDO.setName("向问天");
userDO.setAccount("wentian");
userDO.setPwd("123456");
userDao.save(userDO);
}
@Test
public void testAdd() {
User userDO = new User();
userDO.setId(1L);
userDO.setName("风清扬");
userDO.setAccount("fengqy");
userDO.setPwd("123456");
userDao.save(userDO);
userDO = new User();
userDO.setId(3L);
userDO.setName("东方不败");
userDO.setAccount("bubai");
userDO.setPwd("123456");
userDao.save(userDO);
}
@Test
public void testUpdate() {
User userDO = new User();
userDO.setId(5L);
userDO.setName("小涛");
userDO.setAccount("xiaotao");
userDO.setPwd("123456");
userDao.save(userDO);
}
@Test
public void testDelete() {
userDao.deleteById(5L);
}
@Test
public void testFindAll() {
userDao.findAll();
}
}
运行,就可以看到下列输出,证明一个CURD已经完成,是不是十分的神奇,实际上Spring是利用了AOP以及反射(当然不会直接用反射,会用其他方式如二进制加快读取对象信息)等工具来帮助我们快速实现这个DAO类,只能说屌得一批。
此处还有一个问题,为什么我只是insert,却会输出一个select语句(delete也会),这里我们可以看源码,在SimpleJpaRepository类中,我们可以看到save、delete等方法的源码,可以看到它在执行save执行执行了一次isNew去判断这个对象在数据库中是否已经存在,如果存在,则会进行一次更新操作,所以它在每次insert前都会进行一次select操作。
而delete方法中,一样有这样一段源码,不过方式有些不同,此处涉及一级缓存等概念,可自行查阅。
简单的一对多实现
关联关系是十分常见的一种情况,比如一个作者有多篇文章,一篇文章对应着一个作者,这种关联关系在数据库种以外键的形式存在。而在面向对象中,则可以用以下方式进行表示
Author(作者):
package com.demo.jpademo.entity;
import javax.persistence.*;
import java.util.List;
/**
* @author chenxiaotao
* @version 1.0
* @date 2019/3/25 17:53
**/
@Entity
@Table(name = "author")
public class Author {
/**
* 表格主键,strategy:id策略 (此处为自增)
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
/**
* 姓名
*/
@Column(nullable = false, length = 20)
private String name;
/**
* mapperBy可以理解为主从关系
* 可以简单理解为这些aricleList被author所拥有
*/
@OneToMany(mappedBy = "author", cascade = CascadeType.ALL)
private List<Article> articleList;
}
Article(文章):
package com.demo.jpademo.entity;
import javax.persistence.*;
/**
* @author chenxiaotao
* @version 1.0
* @date 2019/3/25 17:56
**/
@Entity
@Table(name = "article")
public class Article {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, length = 50)
private String title;
/**
* \@Lob 注解为大对象,映射MySQL的LongText类型
* \@Basic(fetch = FetchType.LAZY) 懒加载
*/
@Lob
@Basic(fetch = FetchType.LAZY)
@Column(nullable = false)
private String content;
@ManyToOne(cascade = {CascadeType.MERGE, CascadeType.REFRESH}, optional = false)
@JoinColumn(name = "author_id")
private Author author;
}
这里还涉及到 cascade(配置),可以自行深入了解一下。这样,就创建好了一个一对多个关系,利用navicat可看到数据库中对这两个对象的关系表示为:
总结
SpringBoot-JPA可以加少我们许多的开发工作,可以让我们更加专注于对象与对象之间的关联关系,在快速开发一个应用上十分有效,但可能对需要SQL优化的大型应用不太友好,Mybatis可能对大型应用来说才是更好的选择,所以抛开业务谈技术都是耍流氓,还是得根据实际来,如果你只想做个课程设计,去深入了解下JPA更多的特性(比如:Cascade级联属性,自定义@Query查询),然后利用SpringBoot+JPA+Web就可以快速帮你搭建好一个后端服务了。