jpa简介
JPA (Java Persistence API) Java持久化API。是一套Java官方制定的ORM 方案。Java持久性API(JPA)是Java的一个规范。 它用于在Java对象和关系数据库之间保存数据。 JPA充当面向对象的领域模型和关系数据库系统之间的桥梁。由于JPA只是一个规范,它本身不执行任何操作。 它需要一个实现。 因此,像Hibernate,TopLink和iBatis这样的ORM工具实现了JPA数据持久性规范。实现JPA规范的框架一般最常用的就是
Hibernate
,它是一个重量级框架,学习难度相比Mybatis也更高一些,而SpringDataJPA也是采用Hibernate框架作为底层实现,并对其加以封装。
ORM框架(以下是在ORM机制上运行的一些框架)
- Hibernate (参考:http://www.yiibai.com/hibernate )
- TopLink
- ORMLite
- iBATIS
- JPOX
使用JPA
导入依赖
<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>
yaml配置
spring:
datasource:
url: jdbc:mysql://localhost:3306/mybatis
username: root
password:
driver-class-name: com.mysql.jdbc.Driver
jpa:
show-sql: true
hibernate:
ddl-auto: update
ddl-auto`属性用于设置自动表定义,可以实现自动在数据库中为我们创建一个表,表的结构会根据我们定义的实体类决定,它有4种
- create 启动时删数据库中的表,然后创建,退出时不删除数据表
- create-drop 启动时删数据库中的表,然后创建,退出时删除数据表 如果表不存在报错
- update 如果启动时表格式不一致则更新表,原有数据保留
- validate 项目启动表结构进行校验 如果不一致则报错
JPA常用注解
@Entity
@Table
@Basic
@Column
@GeneratedValue
@Id
@Transient
@Temporal
@OneToMany
@ManyToOne
@ManyToMany
@Query (Spring Data JPA 用法)
@Modifying
创建entity
@Entity
@Data
@Table(name= "user")
public class User {
@Id
@Column(name="id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(name = "name")
private String name;
@Column(name = "pwd")
private String pwd;
}
创建Repository接口
public interface UserRepository extends JpaRepository<User,Integer> {
}
方法名拼接自定义sql
如果我们需要进行条件查询等操作或是一些判断,就需要自定义一些方法来实现,同样的,我们不需要编写SQL语句,而是通过方法名称的拼接来实现条件判断,这里列出了所有支持的条件判断名称:
关键字 | 方法拼接 | 数据库语句 |
---|---|---|
Distinct | findDistinctByLastnameAndFirstname | select distinct … where x.lastname = ?1 and x.firstname = ?2 |
And | findByLastnameAndFirstname | … where x.lastname = ?1 and x.firstname = ?2 |
Or | findByLastnameOrFirstname | … where x.lastname = ?1 or x.firstname = ?2 |
Is ,Equals | findByFirstname ,findByFirstnameIs ,findByFirstnameEquals | … where x.firstname = ?1 |
Between | findByStartDateBetween | … where x.startDate between ?1 and ?2 |
LessThan | findByAgeLessThan | … where x.age < ?1 |
LessThanEqual | findByAgeLessThanEqual | … where x.age <= ?1 |
GreaterThan | findByAgeGreaterThan | … where x.age > ?1 |
GreaterThanEqual | findByAgeGreaterThanEqual | … where x.age >= ?1 |
After | findByStartDateAfter | … where x.startDate > ?1 |
Before | findByStartDateBefore | … where x.startDate < ?1 |
IsNull ,Null | findByAge(Is)Null | … where x.age is null |
IsNotNull ,NotNull | findByAge(Is)NotNull | … where x.age not null |
Like | findByFirstnameLike | … where x.firstname like ?1 |
NotLike | findByFirstnameNotLike | … where x.firstname not like ?1 |
StartingWith | findByFirstnameStartingWith | … where x.firstname like ?1 (参数与附加% 绑定) |
EndingWith | findByFirstnameEndingWith | … where x.firstname like ?1 (参数与前缀% 绑定) |
Containing | findByFirstnameContaining | … where x.firstname like ?1 (参数绑定以% 包装) |
OrderBy | findByAgeOrderByLastnameDesc | … where x.age = ?1 order by x.lastname desc |
Not | findByLastnameNot | … where x.lastname <> ?1 |
In | findByAgeIn(Collection<Age> ages) | … where x.age in ?1 |
NotIn | findByAgeNotIn(Collection<Age> ages) | … where x.age not in ?1 |
True | findByActiveTrue() | … where x.active = true |
False | findByActiveFalse() | … where x.active = false |
IgnoreCase | findByFirstnameIgnoreCase | … where UPPER(x.firstname) = UPPER(?1) |
例子:
public interface UserRepository extends JpaRepository<User,Integer> {
//通过名字查找用户
List<User> findByName(String name);
//通过模糊查询查找用户
List<User> findByNameLike(String name);
//通过名字或id查找用户
List<User> findByNameOrId(String name,Integer id);
//用户是否存在
boolean existsUserByIdAndName(Integer id,String name);
}
关联查询
使用外键
-
@OneToOne
参数相关:fetch = FetchType.LAZY 设置懒加载,这样只有在需要时才会向数据库获取 cascade = CascadeType.ALL ALL:所有操作都进行关联操作 PERSIST:插入操作时才进行关联操作 REMOVE:删除操作时才进行关联操作 MERGE:修改操作时才进行关联操作
-
@OneToMany
-
@ManyToOne
-
@ManyToMany
-
@JoinColumn
注意的点有:
- 自动建立外键
如果用这种方法查询就避免不了建外键,而外键主要是保证数据库数据的完整性和一致性,对于使用外键有好处也有坏处,主要坏处是操作数据方面有了很多的限制,增加了维护成本,需要看你怎么取舍,但很多项目都是业务进行控制数据库的完整性和一致性,不需要建立外键。- 不能分页查询
如果是一对多,多对多,那么只能一次查询全部对应的集合,不能使用分页。
使用@Query注解
@Query比较简单,可以帮你实现分页逻辑,但不能动态条件参数查询,不适合查询条件参数不确定的情况,如果查询条件确定,那么是推荐使用这种。@Query默认使用hsql语言进行查询
HQL | SQL |
---|---|
hql查数据库查类和属性 | sql是通过表和表的列进行查询 |
区分大小写,关键字不区分大小写 | 不区分大小写 |
都可以取别名 | 都可以取别名 |
?占位符(hibernate5之后不支持),一般都是用命名参数,下标从0开始计算 | ?占位符,从顺序1开始计算 |
:命名参数 | 没有命名参数 不支持 |
面向对象的查询语言 | 面向结构查询语言 |
public @interface Query {
/** 定义被执行的sql或者hql */
String value() default "";
/** 分页时用于查询中数量的sql或者hql */
String countQuery() default "";
/** 用原生的分页 */
String countProjection() default "";
/** 使用原生sql,为false时使用hql */
boolean nativeQuery() default false;
/** 定义该查询的名字 */
String name() default "";
/** 数量查询返回的别名 */
String countName() default "";
}
@query返回自定义对象
使用的时hql语法,不支持sql原生
@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.ljw.test.pojo.domain.UserDept(" +
"u.id, u.name, d.id, d.name ) " +
"from User u, Dept d " +
"where u.deptId=d.id")
List<UserDept> findAllForUserDept();
@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>> findAllForMap();
}