JPA 全称 Java Persistence Api, 中文是java 持久化API。
它是当今世界上最流行的ORM (Object-Relational Mapping)框架。
当年我们都感觉传统的ORM框架hibernate 相当的难用和臃肿,特别多联表查询, 然后选择更轻量级和更直观的的Mybatise. 但其实Mybaise 并不是1个ORM框架, 它是拥抱sql的。 对熟悉sql的开发人员更加友好。
当微服务大行其道时, 业务细分下, 通常我们不会在服务中对数据库进行复制的sql查询。 这时mybasise 的优势就不大了, 很多开发人员返回了JPA 的怀抱, 代码更加面向对象是一 回事, 而且JPA也支持用SQL查询。
JPA并不是1个ORM的具体实现, JPA只是定义了1个ORM的规范, 里面只提供了接口, 所以JPA必须配合Hiberate 或者其他ORM框架来使用, 但是hibernate是最常见的。
Mybatise 毕竟是拥抱sql的, 决定了Mybasie 只适用于操作关系型数据库, 例如mysql , oracle, pgsql等…
而JPA可以用相同的接口规范, 用于NOSQL 数据库, 例如Redis,mongo, ES, NEO4j等… 所以JPA是更加有前景的。
在高并发环境下, 一个项目很可能不会只使用单1关系数据库, 很可能我们会用Kafka, redis 等缓存技术, 用ES进行模糊查询, 这时能统一接口规范的JPA就大有发挥空间了。
还有哦, JpaRepostory 接口默认已经帮你写好了很多方法, 例如findById findAll等, 不像mybatise要写很多这种查询接口的…
不同处:
4. JDBC 需要 各个关系型数据库实现(各种数据库driver), JPA需要ORM框架实现(Hibernate)
5. JDBC 使用sql语句与数据库通讯, JPA用面向对象方式, 生成sql后利用JBDC与数据库通讯
6. 所以JPA依赖于JDBC
下面会用1个user service 的例子, 将user service的持久化由 mybatise 改成 JPA + hibernate。
在pom.xml 中, 我们去掉mybatise 的依赖。
并引入
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-jpaartifactId>
dependency>
注意我们使用的是spring-boot-starter-data-jpa, 这个sub pom.xml 已经包含spring-data-jpa 和Hibernate
所以我们不需要另外引入Hibernate 依赖了。
如果我们引入的不是spring-boot-starter-data-jpa, 而是spring-data-jpa, 则还需要引入ORM.
当然mysql的驱动还是需要的
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
dependency>
对于原来的yml数据库, 原来的格式可以通用
但是我们要引入一些jpa的配置
spring:
datasource:
url: jdbc:mysql://43.138.222.61:3306/demo_cloud_user?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true
username: cloud_user
password: '{cipher}323e2265acd321eaec76a88bfa710f5f3673c58f8e6e1bbe2944f08b9518ac0c'
driver-class-name: com.mysql.cj.jdbc.Driver
hikari:
maxLifeTime: 30000
jpa:
show-sql: true
generate-ddl: true
hibernate:
ddl-auto: update
properties:
hibernate:
dialect: org.hibernate.dialect.MySQL8Dialect
下面是一些jpa配置解释
这个配置在开发中可以设置为true, 默认是false, 一旦开启, 则enable了正行工程, 这个是配置自动生成数据库表的前提
一些概念
逆向工程: 根据已存在数据库生成所对应的pojo类
正向工程: 根据已存在的pojo类生成对应的数据库表
create 代表每次运行程序时, 都会把原来的表删除, 然后重新创建表, 慎用
create-drop: 每次运行数据库,都会创建数据表, 使用完后将表删除,慎用
none: 不生效
update: 如果我们的表结构和实体类没有一一映射,或者实体类发生改变, 那么会对数据表更新表结构.
假如数据库表不存在, 则会创建1个表, 在开发中经常使用
validate: 如果实体类和数据表映射不一致, 会抛出异常, 在测试环境经常使用
采用什么数据库方言
其实类比我们生活中的方言,方言就是某个地方的特色语言,这个语言区别于其他地方的语言,比如在江苏,有南通方言、盐城方言、苏州方言,只要你是当地人,你基本能听懂当地的方言。
对于数据库的方言,也是同样的道理,MySQL是一种方言,Oracle也是一种方言,当然他们都遵循SQL规范的,就好比南通方言、盐城方言都遵循普通话规范,然后有各自的一个扩展性和特色。举个例子,MySQL分页是用limit关键字,而Oracle分页是用rownum关键字。
import lombok.Data;
import javax.persistence.*;
@Entity // to be pojo class of Hibernate
@Data
@Table(name = "tb_user")
@ToString
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name="id")
private Long id;
@Column(name="username")
private String username;
@Column(name="address")
private String address;
}
注意要引入javax.persistence包内的注解而不是Hibernate包的
表示这个是1个实体类
表示对应数据库的表名
表示这是1个主键
设置id键生成策略
GenerationType.IDENTITY -> 表示自增列
表示对应的表列名
你看, 借口内什么方法都不用写了
import cn.itcast.user.pojo.User;
import org.springframework.data.jpa.repository.JpaRepository;
/*
* JpaRepository , JpaRepository provide some default crud methods,
* T means the Entity Class,
* ID means the data type of private key , or the attrible which has @Id
*/
public interface UserDao extends JpaRepository<User, Long> {
}
import cn.itcast.user.pojo.User;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import java.util.Optional;
import static org.junit.jupiter.api.Assertions.*;
@Slf4j
@DataJpaTest //default will auto rollback
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
public class UserDaoTest {
@Autowired
private UserDao userDao; //springboot will create an instance of the interface while starting
@Test
@DisplayName("test add User")
public void testAddUser(){
User user = new User();
user.setUsername("Alice5");
user.setAddress("The 5th School");
userDao.save(user);
log.info("User id: {}",user.getId());
assertTrue(0 < user.getId(),"id should be generated");
}
@Test
@DisplayName("find one User")
public void findUserTest1(){
Optional<User> optionaluser = userDao.findById(2L);
User user = optionaluser.get();
log.info(String.valueOf(user));
assertNotNull(user);
}
}