JPA 介绍
JPA 是 Java Persistence API的简称,中文名Java持久层API,是JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。
Sun 引入新的JPA ORM规范出于两个原因:
其一,简化现有Java EE和Java SE应用开发工作
其二,Sun希望整合ORM技术,避免ORM 框架各自为营。
JPA 规范本质上就是一种ORM规范,不是ORM框架
因为JPA并未提供ORM实现,它只是制订了一些规范,提供了一些编程的API接口,但具体实现则由服务厂商来提供实现。
JPA和Hibernate的关系就像JDBC和JDBC驱动的关系,JPA是规范,Hibernate除了作为ORM框架之外,也是一种JPA实现。
JPA 集成使用
Maven依赖
spring-boot
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
spring-boot 配置
spring:
application:
name: myApplicationName
datasource:
url: jdbc:mysql://localhost:3306/mytest?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
jpa:
hibernate:
# 自动更新表结构 自动建表
ddl-auto: update
# 显示sql
show-sql: true
spring.datasource:
指定数据源链接等
数据实体定义
相关资料
@Entity 相关学习介绍 springJPA
JPA @entity@table@index 定义表以及序列案例
@Id 自增设定
通过 jpa 上面的配置
spring:
jpa:
hibernate:
# 自动更新表结构 自动建表
ddl-auto: update
可以实现当通过注解完成实体定义时,启动项目时将建立对应的表结构
实体定义实例
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Index;
import javax.persistence.Table;
import java.util.Date;
// 以下三个为 lombok 与 jpa 无关
@Data
@NoArgsConstructor
@AllArgsConstructor
// javax 自带注解
@Entity(name = "my_test")
// 定义索引列 多个列时 columnList 中使用 , 分割
@Table(indexes = {@Index(columnList = "per_code", name = "index_resources_code")})
public class MyTestEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;
@Column(name = "per_name", length = 64)
private String perName;
@Column(name = "per_code")
private String code;
@Column(name = "business", columnDefinition = "TEXT")
private String business;
/**
* active标记
*/
@Column(name = "active")
private boolean active;
/**
* 创建时间
*/
@Column(name = "create_time")
private Date createTime;
/**
* 更新时间
*/
@Column(name = "update_time")
private Date updateTime;
/**
* 创建者
*/
@Column(name = "creator_id")
private Long creatorId;
/**
* 创建部门
*/
@Column(name = "create_dept_id")
private Long createDeptId;
}
这里 第一次启动 的时候发现 启动失败,报错没有找到索引列,可能建表和字段在建立索引之后,先将索引注解去除,再次启动表自动创建成功,结构如下
可以看到默认长度 Long 为20, string 为 255, length 用于指定长度,columnDefinition 可以额外定义列具体类型和其他定义
加上索引注解,再次启动后启动成功,索引建立成功
附:
@Entity
作用:指定当前类是实体类。
@Table
作用:指定实体类和表之间的对应关系。
属性:
name:指定数据库表的名称
@Id
作用:指定当前字段是主键。
@GeneratedValue
作用:指定主键的生成方式。。
属性:
strategy :指定主键生成策略。
@Column
作用:指定实体类属性和数据库表之间的对应关系
属性:
name:指定数据库表的列名称。
unique:是否唯一
nullable:是否可以为空
inserttable:是否可以插入
updateable:是否可以更新
columnDefinition: 定义建表时创建此列的DDL
secondaryTable: 从表名。
创建DAO 接口层
与其他成形的框架一致,只要继承这些便提供了基本查询方法,同时支持自定义接口
先看最基本的查询方法
import com.dtdream.dthink.dtalent.dmall.openplat.model.entity.MyTestEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.stereotype.Repository;
@Repository
public interface MyTestRepository extends JpaRepository<MyTestEntity, Long>, JpaSpecificationExecutor<MyTestEntity> {
}
JpaRepository, JpaSpecificationExecutor 里面的泛型第一个都是实体本身即可
Repository(仓库,存放处)Specification 规格 Executor执行者
JpaRepository 泛型第二个为 ID 的类型
继承Jpa 的基础类后提供的方法如下
首先创建 Service 并注入Dao 接口对象
import com.dtdream.dthink.dtalent.dmall.openplat.dao.MyTestRepository;
import com.dtdream.dthink.dtalent.dmall.openplat.service.MyTestService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
@Slf4j
public class MyTestServiceImpl implements MyTestService {
@Autowired
private MyTestRepository myTestRepository;
@Override
public void test() {
}
}
jpa 提供的基础查询是 find 开头
统计计数类
删除类
通过Id 单个获取
判断是否存在
保存实体
接口层提供的自动组合方法(IDEA 提示功能)
相当我们站在了更高的角度,面向接口编程,而具体实现由下游实现了Jpa 规范的厂商提供各自的 SQL 化。
通过这种方法也可以实现基本的条件租户完成查询等功能。
例如下面这样:
我们只需要根据接口的单词句意组合即可,find 依旧是查询,get 也支持
By后面即是我们定义的实体的某些属性,当我们不选择属性名后面的其他单词时就是单纯的等于查询。
例如下面相当于:
我要通过active 字段和 code 两个字段查询不给任何其他词汇说明是需要两个字段与后面的值相等即一般的 “=” 查询,两个字段的入参分别为 第一个和第二个入参的值。
同时在服务层中我们可以直接使用该方法,不需要理会实现,实现由底层自动完成拼装。
需要注意的是 单词的拼写必须完全正确同时符合驼峰命名写法用于区分属性字段。
跟在属性后面的其他单词意义等整理如下
前缀,如find、findBy、get、getBy 对剩下部分进行解析
如果方法的最后一个参数是 Sort 或者 Pageable 类型,也会提取相关的信息,以便按规则进行排序或者分页查询。
可能会存在一种特殊情况,比如某一个实体包含一个 user 的属性,也有一个 userName 属性,此时会存在混淆。读者可以明确在属性之间加上 “_” 以显式表达意图,比如 “findByUser_Name()”
在查询时,通常需要同时根据多个属性进行查询,且查询的条件也很多(大于,大于等于,小于,小于等于 等等),Spring Data JPA 为此提供了一些表达条件查询的关键字,大致如下:
And:
等价于 SQL 中的 and 关键字,比如 上面的例子
Or:
等价于 SQL 中的 or 关键字
Between
等价于 SQL 中的 between 关键字 需要指定两个入参,作为区间的边界
LessThan
等价于 SQL 中的 “<” 小于
LessThanEqual
等价于 SQL 中的 “<=” 小于等于
GreaterThan
等价于 SQL 中的">" 大于
GreaterThanEqual
等价于 SQL 中的">=" 大于等于
IsNull
等价于 SQL 中的 “is null”
IsNotNull
等价于 SQL 中的 “is not null”
NotNull
等价于SQL 中的 IsNotNull
Like
等价于 SQL 中的 “like” ,但内容需要自己加入 % 通配符,类似于 入参为 “%内容%”
NotLike
等价于 SQL 中的 “not like”,但内容需要自己加入 % 通配符,类似于 入参为 “%内容%”
OrderBy
等价于 SQL 中的 “order by” 以及OrderBy 后面的 Asc Desc 可以跟在最后面。
Not
等价于 SQL 中的 “! =” 或 <>
In
等价于 SQL 中的 “in”,比如 findByUserNameIn(Collection userList) ,方法的参数可以是 Collection 类型,也可以是数组或者不定长参数;
NotIn
等价于 SQL 中的 “not in” 入参与上方一致
After
与 GreaterThan 功效一致,解释为大于
before
与 LessThan 功效一致,解释为小于
StartingWith
内容以什么开头,类似于like 中 去除最前面的 通配符 %,此时入参不需要加入 %
EndingWith
内容以什么结尾,类似于like 中 去除最后面的 通配符 %,此时入参不需要加入 %
Containing
同样用于模糊查询,内容包含什么 但是也需要手动拼接 通配符 %
Native 本地查询,手动SQL 查询, EntityManager
当有些不满足自己的需要或者觉得还不如自己写,那么久可以如下方式
在Servcei 中注入该类
然后可以通过创建本地 SQL 查询手动自己拼接SQL 完成查询等操作;
例如:
当查询多个字段返回的结果时不得不使用 Object 一个个转化类型,十分繁琐。
当然这是比较差的一种写法不是很推荐,虽然也有优化一点的方式。但也好不到哪里去,最终还是去写 sql 了。
自定义返回类如下:
个别类来源
import org.hibernate.query.internal.NativeQueryImpl;
import org.hibernate.transform.Transformers;
需要的注意的是,这里的自定义返回类中的属性名会和我们上面写的 SQL 去对应
查询的字段别名要和自定义数体类属性对应
即 假如我的实体中有字段叫 createDept 那么 查询中的需要写别名例如 SELECT create_dept AS createDept
需要额外注意的是:
如果数据库定义为 bigint 则 返回接收时定义的实体类型也需要是 BigInteger
如果是时间类型,则需要接收类型需要是 Date 类型
@Query 用法
当 @Query 标记在继承了Repository的自定义接口方法上时,方法名就不需要遵循查询方法命名规则同时也不会去解析校验命名;
注解内定义
使用占位符,序号从 1开始
import org.springframework.data.jpa.repository.Query;
@Repository
public interface UserRepository extends JpaRepository<User, Integer> {
@Query("select u.id, u.name from User u where u.id=?1")
List<User> getById(Integer id);
}
也可以使用参数名
import org.springframework.data.jpa.repository.Query;
@Repository
public interface UserRepository extends JpaRepository<User, Integer> {
@Query("select u.id from User u where u.id= :id")
List<User> getById(@Param("id") Integer id);
}
nativeQuery
当将此值置为 true 时表示使用原生 sql
从定义的实体名查询是 hql 所以 nativeQuery 默认 false
但原生默认返回 object[] 比较麻烦
@Repository
public interface UserRepository extends JpaRepository<User, Integer> {
@Query("select u.id, u.name, d.id, d.name " +
"from User u, Dept d " +
"where u.deptId=d.id", nativeQuery = true)
List<Object[]> findAllForUserDept();
自定义返回类型
使用自定义返回对象时为 返回Java对象 是 hql,所以不支持 nativeQuery 为true
例子:
定义实体
@Data
public class UserDept implements Serializable {
@JsonProperty("user_id")
private Integer userId;
@JsonProperty("user_name")
private String userName;
@JsonProperty("dept_id")
private Integer deptId;
@JsonProperty("dept_name")
private String deptName;
public UserDept(Integer userId, String userName, Integer deptId, String deptName) {
this.userId = userId;
this.userName = userName;
this.deptId = deptId;
this.deptName = deptName;
}
}
from 后面都用对象(hql)
@Repository
public interface UserRepository extends JpaRepository<User, Integer> {
@Query("select new com.test.pojo.domain.UserDept(" +
"u.id, u.name, d.id, d.name ) " +
"from User u, Dept d " +
"where u.deptId=d.id")
List<UserDept> findAllFromUserDept();
@Query("select new map(" +
"u.id as user_id, u.name as user_name, d.id as dept_id, d.name as dept_name) " +
"from User u, Dept d " +
"where u.deptId=d.id")
List<Map<String, Object>> findAllFromMap();
}
更新动作的示例代码关键点示例:
@Modifying
加入后认为是更新动作
@Transactional : 若执行时报错 Transactional is required … update/delete 则需要加上事务注解
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
.........
..........
@Modifying
@Transactional(rollbackFor = Exception.class)
@Query(value = "UPDATE users SET isPublic = :value WHERE id IN :ids" )
void updateIsPublicById(@Param(value = "ids")List<Long> isPublicIdList, @Param(value = "value") String value);
当在Query 最后加入 , nativeQuery = true 时将认为要执行普通的 sql 语句,Query 中的语句所有字段的写法应和表设计中保持一致否则失败,不加入 , nativeQuery = true 时将认为时按实体的属性名来查询即 hql 语句。