Spring Boot2 系列教程(二十三)理解 Spring Data Jpa

  • 3.添加实体类

接下来在项目中添加实体类,如下:

@Entity(name = “t_book”)

public class Book {

private Long id;

private String name;

private String author;

@Id

@GeneratedValue(strategy = GenerationType.IDENTITY)

public Long getId() {

return id;

}

// 省略其他getter/setter

}

首先 @Entity 注解表示这是一个实体类,那么在项目启动时会自动针对该类生成一张表,默认的表名为类名,@Entity 注解的 name 属性表示自定义生成的表名。@Id 注解表示这个字段是一个 id,@GeneratedValue 注解表示主键的自增长策略,对于类中的其他属性,默认都会根据属性名在表中生成相应的字段,字段名和属性名相同,如果开发者想要对字段进行定制,可以使用 @Column 注解,去配置字段的名称,长度,是否为空等等。

  • 4.创建 persistence.xml 文件

JPA 规范要求在类路径的 META-INF 目录下放置 persistence.xml,文件的名称是固定的

<?xml version="1.0" encoding="UTF-8"?>

org.hibernate.jpa.HibernatePersistenceProvider

org.javaboy.Book

<property name=“hibernate.connection.url”

value=“jdbc:mysql:///jpa01?useUnicode=true&characterEncoding=UTF-8”/>

注意:

  1. persistence-unit 的 name 属性用于定义持久化单元的名字, 必填。

  2. transaction-type:指定 JPA 的事务处理策略。RESOURCE_LOCAL:默认值,数据库级别的事务,只能针对一种数据库,不支持分布式事务。如果需要支持分布式事务,使用JTA:transaction-type=“JTA”

  3. class 节点表示显式的列出实体类

  4. properties 中的配置分为两部分:数据库连接信息以及Hibernate信息

  • 5.执行持久化操作

EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory(“NewPersistenceUnit”);

EntityManager manager = entityManagerFactory.createEntityManager();

EntityTransaction transaction = manager.getTransaction();

transaction.begin();

Book book = new Book();

book.setAuthor(“罗贯中”);

book.setName(“三国演义”);

manager.persist(book);

transaction.commit();

manager.close();

entityManagerFactory.close();

这里首先根据配置文件创建出来一个 EntityManagerFactory ,然后再根据 EntityManagerFactory 的实例创建出来一个 EntityManager ,然后再开启事务,调用 EntityManager 中的 persist 方法执行一次持久化操作,最后提交事务,执行完这些操作后,数据库中旧多出来一个 t_book 表,并且表中有一条数据。

2.1.1 关于 JPQL

  1. JPQL 语言,即 Java Persistence Query Language 的简称。JPQL 是一种和 SQL 非常类似的中间性和对象化查询语言,它最终会被编译成针对不同底层数据库的 SQL 查询,从而屏蔽不同数据库的差异。JPQL语言的语句可以是 select 语句、update 语句或 delete 语句,它们都通过 Query 接口封装执行。

  2. Query接口封装了执行数据库查询的相关方法。调用 EntityManager 的 createQuery、create NamedQuery 及 createNativeQuery 方法可以获得查询对象,进而可调用 Query 接口的相关方法来执行查询操作。

  3. Query接口的主要方法如下:

  • int executeUpdate(); | 用于执行update或delete语句。

  • List getResultList(); | 用于执行select语句并返回结果集实体列表。

  • Object getSingleResult(); | 用于执行只返回单个结果实体的select语句。

  • Query setFirstResult(int startPosition); | 用于设置从哪个实体记录开始返回查询结果。

  • Query setMaxResults(int maxResult); | 用于设置返回结果实体的最大数。与setFirstResult结合使用可实现分页查询。

  • Query setFlushMode(FlushModeType flushMode); | 设置查询对象的Flush模式。参数可以取2个枚举值:FlushModeType.AUTO 为自动更新数据库记录,FlushMode Type.COMMIT 为直到提交事务时才更新数据库记录。

  • setHint(String hintName, Object value); | 设置与查询对象相关的特定供应商参数或提示信息。参数名及其取值需要参考特定 JPA 实现库提供商的文档。如果第二个参数无效将抛出IllegalArgumentException异常。

  • setParameter(int position, Object value); | 为查询语句的指定位置参数赋值。Position 指定参数序号,value 为赋给参数的值。

  • setParameter(int position, Date d, TemporalType type); | 为查询语句的指定位置参数赋 Date 值。Position 指定参数序号,value 为赋给参数的值,temporalType 取 TemporalType 的枚举常量,包括 DATE、TIME 及 TIMESTAMP 三个,,用于将 Java 的 Date 型值临时转换为数据库支持的日期时间类型(java.sql.Date、java.sql.Time及java.sql.Timestamp)。

  • setParameter(int position, Calendar c, TemporalType type); | 为查询语句的指定位置参数赋 Calenda r值。position 指定参数序号,value 为赋给参数的值,temporalType 的含义及取舍同前。

  • setParameter(String name, Object value); | 为查询语句的指定名称参数赋值。

  • setParameter(String name, Date d, TemporalType type); | 为查询语句的指定名称参数赋 Date 值,用法同前。

  • setParameter(String name, Calendar c, TemporalType type); | 为查询语句的指定名称参数设置Calendar值。name为参数名,其它同前。该方法调用时如果参数位置或参数名不正确,或者所赋的参数值类型不匹配,将抛出 IllegalArgumentException 异常。

2.1.2 JPQL 举例

和在 SQL 中一样,JPQL 中的 select 语句用于执行查询。其语法可表示为:

select_clause form_clause [where_clause] [groupby_clause] [having_clause] [orderby_clause]

其中:

  1. from 子句是查询语句的必选子句。

  2. select 用来指定查询返回的结果实体或实体的某些属性。

  3. from 子句声明查询源实体类,并指定标识符变量(相当于SQL表的别名)。

  4. 如果不希望返回重复实体,可使用关键字 distinct 修饰。select、from 都是 JPQL 的关键字,通常全大写或全小写,建议不要大小写混用。

在 JPQL 中,查询所有实体的 JPQL 查询语句很简单,如下:

select o from Order o

select o from Order as o

这里关键字 as 可以省去,标识符变量的命名规范与 Java 标识符相同,且区分大小写,调用 EntityManager 的 createQuery() 方法可创建查询对象,接着调用 Query 接口的 getResultList() 方法就可获得查询结果集,如下:

Query query = entityManager.createQuery( “select o from Order o”);

List orders = query.getResultList();

Iterator iterator = orders.iterator();

while(iterator.hasNext() ) {

// 处理Order

}

其他方法的与此类似,这里不再赘述。

2.2 Spring Data 的故事


在 Spring Boot 中,Spring Data Jpa 官方封装了太多东西了,导致很多人用的时候不知道底层到底是怎么配置的,本文就和大伙来看看在手工的 Spring 环境下,Spring Data Jpa 要怎么配置,配置完成后,用法和 Spring Boot 中的用法是一致的。

2.2.1 基本环境搭建

首先创建一个普通的 Maven 工程,并添加如下依赖:

org.springframework

spring-orm

5.0.2.RELEASE

org.springframework

spring-oxm

5.0.2.RELEASE

org.springframework

spring-aop

5.0.2.RELEASE

org.springframework

spring-aspects

5.0.2.RELEASE

mysql

mysql-connector-java

5.1.27

org.springframework

spring-context-support

5.0.2.RELEASE

org.springframework

spring-expression

5.0.2.RELEASE

org.hibernate

hibernate-core

5.2.12.Final

org.hibernate

hibernate-jpamodelgen

5.2.12.Final

com.alibaba

druid

1.0.29

org.springframework.data

spring-data-jpa

1.11.3.RELEASE

这里除了 Jpa 的依赖之外,就是 Spring Data Jpa 的依赖了。

接下来创建一个 User 实体类,创建方式参考 Jpa 中实体类的创建方式,这里不再赘述。

接下来在 resources 目录下创建一个 applicationContext.xml 文件,并配置Spring 和 Jpa,如下:

<context:property-placeholder location=“classpath:db.properties”/>

<context:component-scan base-package=“org.javaboy”/>

true

true

update

org.hibernate.dialect.MySQL57Dialect

<tx:annotation-driven transaction-manager=“transactionManager”/>

<jpa:repositories base-package=“org.javaboy.dao”

entity-manager-factory-ref=“entityManagerFactory”/>

这里和 Jpa 相关的配置主要是三个:

  • 一个是 entityManagerFactory

  • 一个是 Jpa 的事务

  • 一个是配置 dao 的位置

配置完成后,就可以在 org.javaboy.dao 包下创建相应的 Repository 了,如下:

public interface UserDao extends Repository<User, Long> {

User getUserById(Long id);

}

getUserById 表示根据 id 去查询 User 对象,只要我们的方法名称符合类似的规范,就不需要写 SQL,具体的规范一会来说。好了,接下来,创建 Service 和 Controller 来调用这个方法,如下:

@Service

@Transactional

public class UserService {

@Resource

UserDao userDao;

public User getUserById(Long id) {

return userDao.getUserById(id);

}

}

public void test1() {

ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(“applicationContext.xml”);

UserService userService = ctx.getBean(UserService.class);

User user = userService.getUserById(1L);

System.out.println(user);

}

这样,就可以查询到 id 为 1 的用户了。

2.2.2 Repository

上文我们自定义的 UserDao 实现了 Repository 接口,这个 Repository 接口是什么来头呢?

首先来看 Repository 的一个继承关系图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EzlS5c7i-1574048056954)(https://img2018.cnblogs.com/blog/747810/201911/747810-20191118113229247-773546531.png)]

可以看到,实现类不少。那么到底如何理解 Repository 呢?

  1. Repository 接口是 Spring Data 的一个核心接口,它不提供任何方法,开发者需要在自己定义的接口中声明需要的方法 public interface Repository<T, ID extends Serializable> { }

  2. 若我们定义的接口继承了 Repository, 则该接口会被 IOC 容器识别为一个 Repository Bean,进而纳入到 IOC 容器中,进而可以在该接口中定义满足一定规范的方法。

  3. Spring Data可以让我们只定义接口,只要遵循 Spring Data 的规范,就无需写实现类。

  4. 与继承 Repository 等价的一种方式,就是在持久层接口上使用 @RepositoryDefinition 注解,并为其指定 domainClass 和 idClass 属性。像下面这样:

@RepositoryDefinition(domainClass = User.class, idClass = Long.class)

public interface UserDao

{

User findById(Long id);

List findAll();

}

基础的 Repository 提供了最基本的数据访问功能,其几个子接口则扩展了一些功能,它的几个常用的实现类如下:

  • CrudRepository: 继承 Repository,实现了一组 CRUD 相关的方法

  • PagingAndSortingRepository: 继承 CrudRepository,实现了一组分页排序相关的方法

  • JpaRepository: 继承 PagingAndSortingRepository,实现一组 JPA 规范相关的方法

  • 自定义的 XxxxRepository 需要继承 JpaRepository,这样的 XxxxRepository 接口就具备了通用的数据访问控制层的能力。

  • JpaSpecificationExecutor: 不属于Repository 体系,实现一组 JPA Criteria 查询相关的方法

2.2.3 方法定义规范

2.2.3.1 简单条件查询
  • 按照 Spring Data 的规范,查询方法以 find | read | get 开头

  • 涉及条件查询时,条件的属性用条件关键字连接,要注意的是:条件属性以首字母大写

例如:定义一个 Entity 实体类:

class User{

private String firstName;

private String lastName;

使用 And 条件连接时,条件的属性名称与个数要与参数的位置与个数一一对应,如下:

findByLastNameAndFirstName(String lastName,String firstName);

  • 支持属性的级联查询. 若当前类有符合条件的属性, 则优先使用, 而不使用级联属性. 若需要使用级联属性, 则属性之间使用 _ 进行连接.

查询举例:

  • 按照 id 查询

User getUserById(Long id);

User getById(Long id);

  • 查询所有年龄小于 90 岁的人

List findByAgeLessThan(Long age);

  • 查询所有姓赵的人

List findByUsernameStartingWith(String u);

  • 查询所有姓赵的、并且 id 大于 50 的人

List findByUsernameStartingWithAndIdGreaterThan(String name, Long id);

  • 查询所有姓名中包含"上"字的人

List findByUsernameContaining(String name);

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
rtingWithAndIdGreaterThan(String name, Long id);

  • 查询所有姓名中包含"上"字的人

List findByUsernameContaining(String name);

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

[外链图片转存中…(img-3I6eGdyv-1714961280744)]

[外链图片转存中…(img-yrJCHGpu-1714961280745)]

[外链图片转存中…(img-jPkBMDSL-1714961280745)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

  • 23
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值