【SpringBoot】SpringBoot集成Spring Data JPA

简单及记录一下相关的使用,后续会对这部分内容进行补充。
Spring Data JPA 是 Spring 基于 ORM 框架、JPA 规范的基础上封装的一套 JPA 应用框架,底层使用了 Hibernate 的 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>

数据库连接信息的配置:

spring.datasource.url=jdbc:mysql://localhost:3306/learn?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
#######下面的配置是为解决项目启动时遇到的异常
#异常内容:org.hibernate.HibernateException: Access to DialectResolutionInfo cannot be null when 'hibernate.dialect' not set错误
spring.jpa.database-platform=org.hibernate.dialect.MySQL5Dialect
######Hibernate中常用的配置
spring.jpa.properties.hibernate.hbm2ddl.auto=update
# 配置指定对数据库表结构的处理方式,值有:create、create-drop、update、validate 
#
# create:每次加载hibernate的时候,都会重新根据模型生成表。如果表已存在,会先删除该表再生成。
# create-drop:启动项目加载hibernate的时候,会生成表。停止项目时,会把生成的表删除掉。
# update:常用属性。每次加载hibernate的时候,会生成表。如果表存在,会根据模型的属性变化来更新表结构,这个过程不会做删表处理。
# validate:每次加载hibernate的时候,会检查表结构,但不会生成表。
spring.jpa.show-sql=true # 打印sql

创建实体类:

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

import lombok.Data;

@Entity
@Data
@Table(name = "user")
public class User {
	@Id
	private String id;
	private String name;
	private String password;
}

注意:@Entity和@Id注解一定要有。

创建UserRepository继承自JpaRepository<T,TD>:

public interface UserRepository extends JpaRepository<User, String> {
	User findByName(String username);
}

JpaRepository<T,TD>中T表示数据表模型对应的类,TD表示主键id的类型。
注意:

  • 使用自定义的字段来查询时,可以参考文档 jpa查询文档,同时需要注意字段要与数据库中的保持一致原则;例如:数据库中字段为course_id,实体类中为courseId,则方法名应为findByCourseId;这样就可以匹配(方法名的命名规则是由hibernate的解析方式所决定的,如:解析时只会将courseId与数据库中的course_id字段进行匹配,如果实体中定义为courseid,方法名为findByCourseid,则一样会查询失败)。

其余层Service层、Controller层的代码这里就不写了,与常用的写法一致。

2、 @Query注解的使用

使用 @Query注解,当不确定数据库中的字段时使用原生的SQL来进行数据库的查询操作,接下来将上述的查询过程来使用 @Query注解来进行一个改造,具体的改造主要出现在UserRepository中,修改内容如下:

// 将原有的方法名,更改为findA,A为User中不存在的属性
public interface UserRepository extends JpaRepository<User,Long> {
	User findA(String username);
}

这时启动程序会报如下的错误:

Caused by: org.springframework.data.mapping.PropertyReferenceException: No property findA found for type User!

接下来将使用 @Query注解:

public interface UserRepository extends JpaRepository<User,Long> {

	@Query(value="select * from USER where name=?1")
	User findA(String username);
}

启动程序,会报如下的错误:

Caused by: org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected token: * near line 1, column 8 [select * from USER where name=?1]

报这个错的原因是由于,Spring Data JPA是基于hibernate的,所以在进行查询的时候使用的是HQL而非SQL,所以会出现问题,解决办法:
(1)将SQL语句更改为HQL语句,例如上例改为: from USER where name=?1
(2)加上 nativeQuery=true ,意思即为使用原生的SQL语句继续查询操作。
如:@Query(value=“select * from USER where name=?1”,nativeQuery=true)

2.1、在@query中添加条件判断

这里以postgreSQL为例来介绍,日常开发中常会遇到这样的问题,需要进行多条件的判断,这就难免有些条件为空的情况,这时可以在@Query中使用COALESCE函数(相当于mysql中的ISNULL函数)和CASE WHEN condition THEN result [WHEN ...] [ELSE result] END(相当于mysql中的if函数),具体代码如下:

	@Query(value = "SELECT * FROM trn_pub_train " + "WHERE "//
			+ "CASE WHEN COALESCE(?1,'')='' THEN 1=1 ELSE name=?1 END and "//
			+ "CASE WHEN COALESCE(?2,'')='' THEN 1=1 ELSE category_id=?2 END and "//
			+ "CASE WHEN COALESCE(?3,'')='' THEN train_status=?3 ELSE train_status=?3 END ", nativeQuery = true)
	Page<Train> findTrains(String name, String catId, String status, Pageable pageable);

注意:
COALESCE(val1,val2...)返回参数中第一个不为空的参数值,如:COALESCE(null,’’,‘233’),返回‘’
但是在实际的开发中发现如果方法中的参数值为null时,会报org.postgresql.util.PSQLException: 错误: 操作符不存在: character varying = bytea的错误(在sql服务上不会报错),因此可以在代码中对参数进行转化,如下:

		if (StringUtils.isEmpty(name)) {name="";}

在mysql中的写法如下:

// if(?1 !='',x1=?1,1=1) 这里用来判断第一个参数不为'',也可以使用ISNULL()函数来判断null值
@Query(value = "select * from xxx where if(?1 !='',x1=?1,1=1) and if(?2 !='',x2=?2,1=1)" +
            "and if(?3 !='',x3=?3,1=1)  ",nativeQuery = true)
List<XXX> find(String X1,String X2,String X3);
2.1.1、在@query中使用in查询

in查询可以将所有的参数保存在集合中,如下所示:

	@Query(value = "SELECT * FROM trn_pub_train " + "WHERE "//
			+ "CASE WHEN COALESCE(?1,'')='' THEN 1=1 ELSE name=?1 END and "//
			+ "CASE WHEN COALESCE(?2,'')='' THEN 1=1 ELSE category_id=?2 END and "//
			+ "CASE WHEN COALESCE(?3,'')='' THEN train_status=?3 ELSE train_status=?3 END and "
			+ "group_id in (?4)", nativeQuery = true)
	Page<Train> findTrains(String name, String catId, String status,List<String> ids, Pageable pageable);
2.2、使用@query注解实现update/insert/delete

使用@query注解实现update/insert/delete时需要用到另外一个注解@Modifying,如果执行的为update/delete的操作,还需要加上@Transactional注解,如下所示:

@Transactional
@Modifying(clearAutomatically = true)
@Query("UPDATE user SET state='0' WHERE id=?1",nativeQuery = true)
void updateState(String id);

clearAutomatically = true意味着会自动清除实体中保存的数据,为可选参数
insert/delete的操作与上述类似,这里就不写了。

3、使用Example快速实现动态查询

通过一个例子来说明下Example的使用,这里使用JPA中的默认方法findAll

  • 先来看下org.springframework.data.domain.Example<T>一个泛型接口,可以使用Example.of(T probe, ExampleMatcher matcher)方法来创建该接口的实例;
    • probe为实体对象,比如我们要查询User表,那User对象就是可以作为Probe;
    • ExampleMatcher用于设置一些匹配的规则 ;
  • 再来看下findAll方法findAll(Example<S> example, Pageable pageable);,该方法借助于Example接口的实例来实现查询。

接下来介绍下使用过程,代码如下:

// 先来创建Example的实例
Train train=new Train(); // 要查询的数据表对应的实体类
trian.setName("张三");

ExampleMatcher matcher = ExampleMatcher.matching()
				.withIgnorePaths("createTime")// 忽略createTime字段,这里要写实体类中的属性名而不能是数据库中的字段名“create_time”
				.withMatcher("status", GenericPropertyMatchers.contains())// 模糊查询:status like '%' + ?0 + '%'
				.withIgnoreNullValues();// 忽略值为null的字段
Exampl<Train> example=Example.of(train,matcher);
Rageable pageable=PageRequest.of(pageNum, pageSize, Sort.by(Direction.DESC, "createTime"));
//Sort.by(Direction.DESC, "createTime") 这里要写实体类中的属性名而不能是数据库中的字段名“create_time”
// 执行查询
List<Train> list=trainRepository.findAll(example,pageable);

上述代码中两处需要使用实体类中的属性名的地方,如果使用数据库中的字段名,会提示No property created found for type
关于Example的更多使用可以参考官网相关内容:Query by Example 这种方式由于本身的局限性:

  • 不支持过滤条件分组。即不支持过滤条件用 or(或) 来连接,所有的过滤查件,都是简单一层的用 and(并且) 连接。
  • 仅支持字符串的开始/包含/结束/正则表达式匹配和 其他属性类型的精确匹配。查询时,对一个要进行匹配的属性(如:姓名 name),只能传入一个过滤条件值,如以Customer为例,要查询姓“刘”的客户,“刘”这个条件值就存储在表示条件对象的Customer对象的name属性中,针对于“姓名”的过滤也只有这么一个存储过滤值的位置,没办法同时传入两个过滤值。正是由于这个限制,有些查询是没办法支持的,例如要查询某个时间段内添加的客户,对应的属性是 addTime,需要传入“开始时间”和“结束时间”两个条件值,而这种查询方式没有存两个值的位置,所以就没办法完成这样的查询。

推荐博文:
springdata jpa使用Example快速实现动态查询
SpringBoot2(五):Spring Data JPA 使用详解
QueryByExampleExecutor接口的查询

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值