ORM 概述
ORM(Object-Relational Mapping) 表示对象关系映射。在面向对象的软件开发中,通过
ORM,就可以把对象映射到关系型数据库中。只要有一套程序能够做到建立对象与数据库的关联,
操作对象就可以直接操作数据库数据,就可以说这套程序实现了 ORM 对象关系映射
简单的说:ORM 就是建立实体类和数据库表之间的关系,从而达到操作实体类就相当于操作数据库
表的目的。
为什么使用 ORM
当实现一个应用程序时(不使用 O/R Mapping),我们可能会写特别多数据访问层的代码,从数据库保存数
据、修改数据、删除数据,而这些代码都是重复的。而使用 ORM 则会大大减少重复性代码。对象关系映射(Object Relational Mapping,简称 ORM),主要实现程序对象到关系数据库数据的映射。
常见 ORM
常见的 orm 框架:Mybatis(ibatis)、Hibernate、Jpa
hibernate 与 与 JPA 的概述
hibernate
Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,它将POJO与
数据库表建立映射关系,是一个全自动的 orm 框架,hibernate 可以自动生成 SQL 语句,自动执行,使得 Java
程序员可以随心所欲的使用对象编程思维来操纵数据库。
JPA 概述
JPA 的全称是 Java Persistence API, 即 Java 持久化 API,是 SUN 公司推出的一套基于 ORM 的规范,
内部是由一系列的接口和抽象类构成。
JPA 通过 JDK 5.0 注解描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。
JPA 的优势
- 标准化
JPA 是 JCP 组织发布的 Java EE 标准之一,因此任何声称符合 JPA 标准的框架都遵循同样的架构,
提供相同的访问 API,这保证了基于 JPA 开发的企业应用能够经过少量的修改就能够在不同的 JPA 框架下运行。 - 容器级特性的支持
JPA 框架中支持大数据集、事务、并发等容器级事务,这使得 JPA 超越了简单持久化框架的局限,在企
业应用发挥更大的作用。 - 简单方便
JPA 的主要目标之一就是提供更加简单的编程模型:在 JPA 框架下创建实体和创建 Java 类一样简单,没
有任何的约束和限制,只需要使用 javax.persistence.Entity 进行注释,JPA 的框架和接口也都非常简单,
没有太多特别的规则和设计模式的要求,开发者可以很容易的掌握。JPA 基于非侵入式原则设计,因此可以很容易
的和其它框架或者容器集成 - 查询能力
JPA 的查询语言是面向对象而非面向数据库的,它以面向对象的自然语法构造查询语句,可以看成是
Hibernate HQL 的等价物。JPA 定义了独特的 JPQL(Java Persistence Query Language),JPQL 是 EJB
QL的一种扩展,它是针对实体的一种查询语言,操作对象是实体,而不是关系数据库的表,而且能够支持批量更新
和修改、JOIN、GROUP BY、HAVING 等通常只有 SQL 才能够提供的高级查询特性,甚至还能够支持子查询。 - 高级特性
JPA 中能够支持面向对象的高级特性,如类之间的继承、多态和类之间的复杂关系,这样的支持能够让开
发者最大限度的使用面向对象的模型设计企业应用,而不需要自行处理这些特性在关系数据库的持久化。JPA 与 与 hibernate 的关系
JPA 规范本质上就是一种 ORM规范,注意不是 ORM框架——因为JPA 并未提供ORM 实现,它只是制订了一些规范,提供了一些编程的 API 接口,但具体实现则由服务厂商来提供实现。
JPA 和 Hibernate 的关系就像 JDBC 和 JDBC 驱动的关系,JPA 是规范,Hibernate 除了作为 ORM 框架之
外,它也是一种 JPA 实现。JPA 怎么取代 Hibernate 呢?JDBC 规范可以驱动底层数据库吗?答案是否定的,也
就是说,如果使用 JPA 规范进行数据库操作,底层需要 hibernate 作为其实现类完成数据持久化工作。springData-Jpa的使用
(1)引入依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><!-- 同时操作数据库,必然少不了数据库驱动包 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency>
(2)提供数据库配置
spring: application: name: tensquare-user #指定服务名 datasource: driverClassName: com.mysql.jdbc.Driver url: jdbc:mysql://192.168.108.140:3306/tensquare_user?characterEncoding=UTF8 username: root password: root jpa: database: MySQL show-sql: true(3)创建数据访问接口
这里有几个关键点:public interface UserDao extends JpaRepository<User, String>, JpaSpecificationExecutor<User> {}
- 继承JpaRepository并在范型上指定实体类类型以及主键Id的数据类型
- 如果需要用到分页,还需继承JpaSpecificationExecutor同样范型传实体类类型
- 通过接口就能直接使用一些简单的方法了,同时如果原有方法不够用了,spring-data-jap可以通过一套通过方法名命规则来定义方法,实现一下更高级的数据操作
当原生的方法,或者通过方法名命规则不能达到业务需求的时候,也可以通过编写原生sql语句的方式来实现数据的访问:如下
通过使用@Query注解,value属性来指定要执行的Sql语句,nativeQuery属性设为true表示来支持自定义sql语 句的执行,并且需要再使用@Modiying注解,另外方法参数要与SQL语句中的占位符对应/** * 跟新当前用户关注数 * * @param num * @param userId */ @Modifying @Query(value = "UPDATE tb_user SET followcount = followcount + ? WHERE id = ?", nativeQuery = true) void updateFollowCount(int num, String userId);(4)提供关系映射实体类
关系映射实体类实现序列化接口
- 在实体类上使用@Entity注解,@Table注解name属性指定数据库表名
- 域(实体类属性)主键Id也可能不叫id属性,但只要是主键就行,需使用@Id注解声明
- 其它域(属性)如果属性名和数据库表字段一直,可以啥也不用写,如果不一致,需要使用@Column注解name属性指定数据库字段名的方式来和实体类中的属性做映射
(5)分页查询
总结:如上代码案例可以看出,使用SpringData-JPA进行条件查询,只需要通过Specification对象进行过滤条件的封装,如果还需要分页的话,再传递一个Pageable 类型的对象,而Pageable对象可以通过PageRequest.of()方法得到,最终返回一个Page类型的对象/** * 条件分页查询 * * @param label * @param page * @param size * @return */ @RequestMapping(value = "/search/{page}/{size}") public Result pageQuery(@RequestBody Label label, @PathVariable int page, @PathVariable int size) { Page<Label> pageData = labelService.pageQuery(label, page, size); return new Result(true, StatusCode.OK, "查询成功!" , new PageResult<>(pageData.getTotalElements(), pageData.getContent())); } /** * 按条件分页查询 * @param label * @param page * @param size * @return */ public Page<Label> pageQuery(Label label, int page, int size) { Pageable pageable = PageRequest.of(page - 1, size); return labelDao.findAll((Specification<Label>) (root, criteriaQuery, criteriaBuilder) -> { // 定义一个集合用来封装条件 List<Predicate> paramList = new ArrayList<>(); // 过滤标签名称条件 if (StringUtils.isNotBlank(label.getLabelName())) { Predicate predicate = criteriaBuilder.like(root.get("labelName").as(String.class), "%" + label.getLabelName() + "%"); paramList.add(predicate); } // 过滤状态state条件 if (StringUtils.isNotBlank(label.getState())) { Predicate predicate = criteriaBuilder.equal(root.get("state").as(String.class), label.getState()); paramList.add(predicate); } // 将条件集合转为数组 Predicate[] predicates = new Predicate[paramList.size()]; paramList.toArray(predicates); return criteriaBuilder.and(predicates); }, pageable); }
