SpringData进阶篇
- 一:故事背景
- 二:自定义操作
- 2.1 JPQL和SQL
- 2.1.1 接口内定义
- 2.1.2 调用
- 2.2.3 SQL 方式查询
- 2.2 规定方法名
- 2.2.1 普通查询规则
- 2.2.2 修饰查询
- 2.3 Query By Example
- 2.3.1 Repository继承QueryByExampleExecutor
- 2.3.2 具体使用
- 2.3.2 ExampleMatcher类中常用的方法及其作用
- 2.4 Specifications
- 三:多表关联
- 3.1 一对一
- 3.1.1 单向一对一
- 3.1.2 双向一对一
- 3.2 一对多
- 3.3 多对一
- 3.4 多对多
- 四:总结&提升
一:故事背景
这是我的第三遍SpringData的文章,在上一篇文章中,我们讲述了通过XML和JavaConfig两种方式配置项目,使用SpringData,在不适用Sql语句的情况下,进行了CRUD,这篇文章我们将会从两个部分继续讲述SpringData,一方面是为了满足我们复杂的业务进行的自定义操作,另一方面是为了处理表与表,属性与属性的关系,进行多表关联。此篇文章目的为让大家提升SpringData的可用性,使用其支持复杂业务。
二:自定义操作
2.1 JPQL和SQL
JPQL是一种基于对象模型的查询语言,用来在Java持久化框架执行数据库操作,这里我们直接给出对应的实例,展示如何使用JPQL进行数据操作
2.1.1 接口内定义
JPQL主要通过@Query注解来进行实现查询,其写法如下:
@Query("from User where userName=?1")
List<User> findUserByName(String userName);
@Query("from User where userName=:userName")
List<User> findUserByName2(@Param("userName") String userName);
上文给出了两种指定参数的方法,第一种是通过?加索引的方式,第二种是通过@Param指定参数名的方式。
如果是增删改操作,需要在生命的查询方法上添加@Modifying,告诉jpa这个一个修改语句,并且还要在业务层添加 @Transactional注解,保证操作的事务性
@Query("update User u set u.userName=:userName where u.id=:id")
@Modifying
int updateUserById(@Param("id") Long id,@Param("userName") String userName);
2.1.2 调用
@Test
public void test(){
List<User> name = iUserRepository2.findUserByName("郝立琢");
for (User user : name) {
System.out.println(user.getUserName());
}
}
@Test
@Transactional
public void testU(){
int i = iUserRepository2.updateUserById(1L,"郝666");
System.out.println("更新成功了~~~"+i);
}
这里推荐一个IDEA的插件 JPA Buddy,这个插件可以给我们提示,让我们编码更加简单
2.2.3 SQL 方式查询
通过@Query的nativeQuery属性我们可以指定使用SQL查询,使用SQL查询一定要注意SQL的正确性。不要写习惯了JPQL直接使用JPQL进行对应的编写。
@Query(value = "select * from user",nativeQuery = true)
List<User> selectAll();
2.2 规定方法名
规定方法名是一种更加简单的方式,我们只需要在接口能按照对应的规则设置对应方法,就可以直接使用
2.2.1 普通查询规则
2.2.2 修饰查询
这种方法免去了自己编写JPQL,相比较而言更加的简单。
2.3 Query By Example
Query by Example 翻译成中文的意思是 示例查询,其允许动态创建查询,并且并不需要编写包含字段名称的查询,其支持字符串类型的灵活匹配以及其他类型的精确匹配。但不支持嵌套或者分组的属性约束。
以下为具体示例:
2.3.1 Repository继承QueryByExampleExecutor
将Repository继承QueryByExampleExecutor通过继承此接口可以可以使用示例查询
public interface IUserRepository3 extends PagingAndSortingRepository<User,Long>,QueryByExampleExecutor<User> {
}
2.3.2 具体使用
具体使用的过程中主要涉及到两个部分,第一个部分是Example类,用来声明具体的示例,通过ExampleMatcher来设置具体的匹配条件。
@ContextConfiguration(classes = SpringDataJpaConfig.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class SpringDataTest4 {
private static final Logger logger = Logger.getLogger(SpringDataTest4.class.getName());
@Autowired
private IUserRepository3 iUserRepository3;
@Test
public void test() {
User user = new User();
user.setId(1l);
user.setUserName("郝立琢");
//这里注意使用的是org.springframework.data.domain.Example而不是hibernate的Example
Example<User> example = Example.of(user);
List<User> list = (List<User>) iUserRepository3.findAll(example);
logger.info(String.valueOf(list));
}
@Test
public void test2() {
User user = new User();
user.setUserName("haolizhuo");
//匹配器,设置多条件匹配
ExampleMatcher matcher = ExampleMatcher.matching().
//忽略id
withIgnorePaths("id").
//忽略大小写
withIgnoreCase("userName");
//这里注意使用的是org.springframework.data.domain.Example而不是hibernate的Example
Example<User> example = Example.of(user,matcher);
List<User> list = (List<User>) iUserRepository3.findAll(example);
logger.info(String.valueOf(list));
}
}
2.3.2 ExampleMatcher类中常用的方法及其作用
2.4 Specifications
在之前使用Query by Example只能针对字符串进行条件设置,那如果希望对所有类型支持,可以使用Specifications,下面给出其基本写法。
@Test
public void test() {
//使用内部类的方式直接进行查询
List<User> all = iUserRepository4.findAll((Specification<User>) (root, query, criteriaBuilder) -> {
// root 可以用来获取某个列
// query where 设置各种条件
// criteriaBuilder 组合(order by ,where)
Path<String> id = root.get("id");
Path<String> userName = root.get("userName");
//参数一,为那个字段设置条件、参数2 为条件设置的值
Predicate equal = criteriaBuilder.equal(userName, "郝立琢");
return equal;
});
logger.info(all.get(0).getUserName());
}
此种方式主要是通过Specification接口,里面有三个重要参数第一个是root用来获取某个列、query 设置各种条件、criteriaBuilder 组合进行查询。通过Specification我们可以定义个性化查询,但是使用起来非常的复杂。
三:多表关联
3.1 一对一
定义一对一关系,可以将两张表进行关联,通过配置,无论是增删改查都可以直接通过对应的数据对象进行直接使用。其实现主要通过@OneToOne注解和它的一些属性实现。
- cascade属性:
ALL:所有
PERSIST:插入
MERGE:修改
REMOVE:删除 - fetch属性:
EAGER:默认是EAGER立即加载
LAZY 懒加载是用到才会进行查询
懒加载必须要配置事务,否则查询完之后 Session对象就会释放,下面使用的时候就无法加载了 - orphanRemoval属性:
关联移除(通常在修改的时候用到),一旦将关联的数据设置为null,会删除关联数据 - optional属性:
默认为true,如果设置为false则限制关联的对象为null - mappedBy 属性:
将外键约束执行另一方维护,在一对一双向的时候需要进行配置
值 = 另一方关联属性名
3.1.1 单向一对一
单向一对一指的是在一个表内,关联另外一张表,比如我们有一章Customer表还有一章Account表,在Customer设置一个外键存储Account的id。表示在Customer表内单向一对一关联Account表。
@Data
@Entity
@Table(name = "customer")
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String userName;
private String phone;
@OneToOne(cascade = CascadeType.PERSIST,fetch = FetchType.LAZY)
/**
指定外键Id
**/
@JoinColumn(name = "account_id")
private Account account;
}
此示例中我们通过@OneToOne注解指定了一对一的关系,通过@JoinColumn指定了对应的Account表的id,将两张表进行了单向关联,在我们查询Customer 表的时候,就会自动将account的数据查出来
3.1.2 双向一对一
双向一对一指的时,在我们上述将的单向一对一的基础上,Account表内也专门设置一个字段存储了Customer 表的主键,这样在查询Account的数据的时候也可以同样将Customer表的数据带上来。
这里要注意,我们可以通过@OneToOne注解的mappedBy属性来指定某一方进行依赖约束即可。
@Data
@Entity
@Table(name = "account")
public class Account {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String accountName;
@OneToOne(mappedBy = "account")
@JoinColumn(name = "customer_id")
private Customer customer;
}
3.2 一对多
一对多的方式,我们需要在一的那一方维护多的一方的一个List类型的对象,并且通过@OneToMany指定一对多关系,通过@JoinColumn注解指定一个维护的id,这个id会存储到多的一方的表内,表示一对多的关联关系。一对多的关联关系适合用于查询使用。
/**
一对多的信息,默认就是懒加载。与一对一相反
**/
@OneToMany
@JoinColumn(name="message_id")
private List<Message> messageList;
比如我们新建一个Customer类,一个Message类。Customer类与Message类是一对多的关系,我们可以在Customer类中声明一个List的集合表示一对多的关系。
这里需要注意的是一对多的关系默认是懒加载,这和一对一正好相反,因为一对多的关系关联的数据往往比较多,通过懒加载的方式(注意查询使用事务注解)可以很大程度上提升效率。
3.3 多对一
多对一的方式,我们需要在多的一方,声明一个,一的一方的对象。用上述例子而言指的是在Message类内声明一个Customer类型的对象。
/**
多对一的关系,适合存储多条信息的时候
**/
@ManyToOne
private Customer customer;
由于我们上述例子在一对多时已经维护了@JoinColumn(name=“message_id”),这里便可以不在重复指定。
多对一的关系更加适合存储多的一方的信息时使用,通过对customer进行赋值,将对应的信息进行关联。
3.4 多对多
多对多关系会产生对应的中间表,通过中间表维护多对多关系。
/**
多对多的关系将会产生中间表,通过中间表维护多对多关系
**/
@ManyToMany
@JoinTable(
name = "customer_role",
joinColumns = {@JoinColumn(name = "c_id")},
inverseJoinColumns = {@JoinColumn(name = "r_id")}
)
private List<Role> roles;
四:总结&提升
本文我们讲述了SpringData的多种查询方式,以及设计到多表之间如何进行关联,关联之后的数据操作。通过此篇文章,相信你已经学会了如何使用SpringData进行开发。
我们下面的文章将会分析底层原理,以及具体项目中的整合方式,大家感兴趣可以持续关注我的专栏~