Spring全家桶之SpringData——Spring Data JPA

Spring Data JPA

  • 一、介绍
    • 常用注解
      • 实体类中
      • 测试类中
    • 相关术语
  • 二、实战应用 :搭建Spring Data JPA项目
    • 介绍
    • 步骤
      • 1. 导入jar 两个核心jar 加上3个sl4j的jar
      • 2. 在配置文件中开启Spring Data JPA的命名空间以及它的约束
      • 3. 在配置文件中添加Spring Data JPA的配置
      • 4. 让dao接口继承 JpaRepository接口
      • 5 进行测试
  • 三、Spring Data JPA 的接口继承结构
  • 四、Spring Data JPA 的运行原理
  • 五、Repository接口
    • 基于方法名称命名规则查询
      • 等值查询
      • like查询
      • 多条件查询
      • 其他类型的查询
    • 基于@query注解的查询
      • 等值查询
      • like查询
      • 多条件查询
      • 更新操作
  • 六、CrudRepository接口
    • 创建接口
    • 测试代码
  • 七、PagingAndSortingRepository接口
    • 创建接口
    • 测试代码
      • 分页处理
      • 排序处理
  • 八、JpaRepository 接口
    • 创建接口
    • 测试代码
  • 九、JpaSpecificationExecutor接口
    • 创建接口
    • 测试代码
      • 单条件查询
      • 多条件查询(and)
      • 多条件查询(and/or另一种写法)
      • 带条件分页查询
      • 带条件的排序查询
      • 带条件与排序的分页查询
  • 十、用户自定义Repository接口
    • 创建接口
    • 创建实现类
    • 使用接口
    • 编写测试代码
  • 十一、关系映射操作
    • 一对一的关联操作
      • 创建用户实体
      • 创建角色实体
      • 测试代码
    • 一对多的关联操作
      • 创建用户实体
      • 创建角色实体
      • 测试代码
    • 多对多的关联操作
      • 创建菜单实体
      • 创建角色实体
      • 创建接口
      • 测试代码

一、介绍

Spring Data JPA:Spring Data JPA 是spring data 项目下的一个模块。提供了一套基于JPA标准操作数据库的简化方案。底层默认的是依赖Hibernate JPA 来实现的。
Spring Data JPA 的技术特点:我们只需要定义接口并继承Spring Data JPA 中所提供的接口就可以了。不需要编写接口实现类。
SpringDataJPA官网

常用注解

实体类中

注解名称 作用
@Entity 表示当前类是实体类
@Table(name=“t_roles”) 表示开启正向工程,运行后会自动创建 t_roles这个表
@Id 表示当前属性作为该表的主键
@GeneratedValue(strategy=GenerationType.IDENTITY) 配合@Id一起使用,表示令当前主键自增
@Column(name=“userid”) 表示将当前属性添加到数据库表中 ,列名为userid
@OneToOne(mappedBy=“roles”) 表示当前roles对象与另一张表中(@JoinColumn)相等的数据
@OneToOne(cascade=CascadeType.PERSIST) 创建级联操作 ,一般在存在外键的那个列 ,一般与@JoinColumn连用
@OneToMany() 指一对多关系 .
cascade=CascadeType.PERSIST 表示对该外键开启级联操作 ,mappedBy 表示被该外键对象属性引用
@ManyToMany() 指多对多关系 .
cascade=CascadeType.PERSIST 表示对该外键开启级联操作
mappedBy 表示被该外键对象属性引用
fetch=FetchType.EAGER : 放弃延迟加载,解决多对多查询时,查询闻不到对象的问题
@JoinColumn(name=“roles_id”) 在本表创建roles_id 这个栏位开启外键并维护这个外键
一般与级联操作的属性同时出现
@JoinTables 映射中间表信息,配置在哪一侧都可以,多对多
joinColumns: 当前表主键所关联的中间表中的外键字段
inverseJoinColumns :建立另一张表在中间表中的外键字段

举例:
@JoinTable(name=“t_roles_menus”,joinColumns=@JoinColumn(name=“role_id”),inverseJoinColumns=@JoinColumn(name=“menu_id”))

补充 :

正向工程: 通过实体类和查询方法自动创建数据库表 如Hibernate与Hibernate Jpa
逆向工程: 通过数据库表自动创建对应的实体类以及查询方法 逆向工程的使用

测试类中

注解名称 作用
@RunWith(SpringJUnit4ClassRunner.class) 表示运行当前类时 ,同时调用SpringJUnit4ClassRunner类(测试类)
@ContextConfiguration(“classpath:applicationContext.xml”) 表示运行该类时 ,同时扫描的的配置文件
@Test 表示当前方法开启Junit调试
@Transactional 声明开启事务 , 对于事务提交方式默认的是回滚。
@Rollback(false) 取消自动回滚

相关术语

持久化
持久化是将程序数据在持久状态和瞬时状态间转换的机制。通俗的讲,就是瞬时数据(比如内存中的数据,是不能永久保存的)持久化为持久数据(比如持久化至数据库中,能够长久保存)。

持久层
所谓“持久层”,多指Dao层或者Mapper层 ,也就是在系统逻辑层面上,专注于实现数据持久化的一个相对独立的领域(Domain),是把数据保存到可掉电式存储设备中。持久层是负责向(或者从)一个或者多个数据存储器中存储(或者获取)数据的一组类和组件。

持久化状态
持久化状态对象表示在数据库中有对应id的记录,同时在session缓存中也存在对应ID的对象 ,可以随时对进行增删改查操作操作

Hibernate三种状态 介绍
itransient 临时状态 类似:没有编号的公司临时工
说明:临时状态表示在数据库中没有对应id的记录,同时在session缓存中也不存对应ID的对象
persistent 持久化状态 类似:是有编号的公司正式员工
说明:持久化对象表示在数据库中有对应id的记录,同时在session缓存中也存在对应ID的对象;
detached 游离状态 类似:休假中的公司正式员工(与公司失去联系)
说明:游离状态表示在数据库中有对应ID的记录,但在session缓存中不存在对应ID的对象;

二、实战应用 :搭建Spring Data JPA项目

介绍

Spring Data JPA 实现无需在dao层实现类书写代码即可实现对数据库的操作
使用的查询语言是 HQL语言
本项目是基于 Hibernate Jpa项目上构建 ,Hibernate Jpa查看上篇博文

Spring全家桶之SpringData——Spring Data JPA_第1张图片

步骤

1. 导入jar 两个核心jar 加上3个sl4j的jar

Spring全家桶之SpringData——Spring Data JPA_第2张图片
至此所有jar包如下

antlr-2.7.7.jar
aopalliance.jar
aspectjrt.jar
aspectjweaver.jar
c3p0-0.9.2.1.jar
commons-logging-1.1.1.jar
dom4j-1.6.1.jar
geronimo-jta_1.1_spec-1.1.1.jar
hibernate-c3p0-5.0.7.Final.jar
hibernate-commons-annotations-5.0.1.Final.jar
hibernate-core-5.0.7.Final.jar
hibernate-entitymanager-5.0.7.Final.jar
hibernate-jpa-2.1-api-1.0.0.Final.jar
jandex-2.0.0.Final.jar
javassist-3.18.1-GA.jar
jboss-logging-3.3.0.Final.jar
log4j-1.2.16.jar
mchange-commons-java-0.2.3.4.jar
mysql-connector-java-5.1.7-bin.jar
slf4j-api-1.6.1.jar
slf4j-log4j12-1.7.2.jar
spring-aop-4.2.0.RELEASE.jar
spring-aspects-4.2.0.RELEASE.jar
spring-beans-4.2.0.RELEASE.jar
spring-context-4.2.0.RELEASE.jar
spring-core-4.2.0.RELEASE.jar
spring-data-commons-1.11.0.RELEASE.jar
spring-data-jpa-1.9.0.RELEASE.jar
spring-expression-4.2.0.RELEASE.jar
spring-jdbc-4.2.0.RELEASE.jar
spring-orm-4.2.0.RELEASE.jar
spring-test-4.2.0.RELEASE.jar
spring-tx-4.2.0.RELEASE.jar

2. 在配置文件中开启Spring Data JPA的命名空间以及它的约束

xmlns:jpa="http://www.springframework.org/schema/data/jpa"

http://www.springframework.org/schema/data/jpa 
http://www.springframework.org/schema/data/jpa/spring-jpa.xsd

3. 在配置文件中添加Spring Data JPA的配置

	 
	 
	<jpa:repositories base-package="ah.szxy.dao"/>

至此,完整的配置文件如下


<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:jpa="http://www.springframework.org/schema/data/jpa"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
		http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

	
	<context:property-placeholder
		location="classpath:.properties" />

	
	<bean id="dataSource"
		class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="jdbcUrl" value="${jdbc.url}" />
		<property name="driverClass" value="${jdbc.driver.class}" />
		<property name="user" value="${jdbc.username}" />
		<property name="password" value="${jdbc.password}" />
	bean>

	
	<bean id="entityManagerFactory"
		class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<property name="jpaVendorAdapter">
			<bean
				class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
				
				
				<property name="database" value="MYSQL" />
				
				<property name="generateDdl" value="true" />
				
				<property name="showSql" value="true" />
			bean>
		property>
		
		<property name="packagesToScan">
			<list>
				<value>ah.szxy.pojovalue>
			list>
		property>
	bean>
	
	<bean id="transactionManager"
		class="org.springframework.orm.jpa.JpaTransactionManager">
		<property name="entityManagerFactory" ref="entityManagerFactory" />
	bean>
	

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

	
	<context:component-scan base-package="ah.szxy" />

	 
	 
	<jpa:repositories base-package="ah.szxy.dao"/>
beans>

db.properties(数据库连接参数)

jdbc.url=jdbc:mysql://localhost:3306/springdata
jdbc.driver.class=com.mysql.jdbc.Driver
jdbc.username=root
jdbc.password=root

实体类不变 ,代码如下

package ah.szxy.pojo;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="t_users")
public class Users implements Serializable{
	//依次为主键 ,自增长 ,列名
	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	@Column(name="userid")
	private Integer userid;
	
	@Column(name="username")
	private String username;
	
	@Column(name="userage")
	private Integer userage;

	public Integer getUserid() {
		return userid;
	}

	public void setUserid(Integer userid) {
		this.userid = userid;
	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public Integer getUserage() {
		return userage;
	}

	public void setUserage(Integer userage) {
		this.userage = userage;
	}

	@Override
	public String toString() {
		return "Users [userid=" + userid + ", username=" + username + ", userage=" + userage + "]";
	}

	public Users(Integer userid, String username, Integer userage) {
		super();
		this.userid = userid;
		this.username = username;
		this.userage = userage;
	}

	public Users() {
		super();
	}
	
	
}

4. 让dao接口继承 JpaRepository接口

JpaRepository : 接口中没有方法 ,没有接口实现类 ,接口内置
因为是自动生成的接口以及实现类 ,所以只能进行简单的数据库操作
可以调用的超类的方法如下

Spring全家桶之SpringData——Spring Data JPA_第3张图片
Spring全家桶之SpringData——Spring Data JPA_第4张图片

接口类

package ah.szxy.dao;


import org.springframework.data.jpa.repository.JpaRepository;

import ah.szxy.pojo.Users;

// JpaRepository<实体类, 主键类型>
public interface UserDao extends  JpaRepository<Users, Integer>{
	
}

5 进行测试

测试类

package ah.szxy.test;

import java.util.List;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;

import ah.szxy.dao.UserDao;
import ah.szxy.pojo.Users;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestHibernate {

	@Autowired
	private UserDao userDao;

	/**
	 * 添加用户
	 */
	@Test
	@Transactional // 在测试类对于事务提交方式默认的是回滚。
	@Rollback(false) // 取消自动回滚
	public void testInsertUsers() {
		Users users = new Users();
		users.setUserage(23);
		users.setUsername("jpa");
		this.userDao.save(users);
	}


}

三、Spring Data JPA 的接口继承结构

Spring全家桶之SpringData——Spring Data JPA_第5张图片

四、Spring Data JPA 的运行原理

通过运行测试类 ,来简单的帮我们理解一下其运行原理

@PersistenceContext(name="entityManagerFactory")
	private EntityManager em;
	@Test
	public void test1(){
	//org.springframework.data.jpa.repository.support.SimpleJpaRepository@fba8bf
	//System.out.println(this.usersDao);
	//class com.sun.proxy.$Proxy29 代理对象是基于JDK 的动态代理方式创建的
	//System.out.println(this.usersDao.getClass());
	JpaRepositoryFactory factory = new JpaRepositoryFactory(em);
	//getRepository(UsersDao.class);可以帮助我们为接口生成实现类。而这个实现类是SimpleJpaRepository 的对象
	//要求:该接口必须要是继承Repository 接口
	UserDao ud = factory.getRepository(UserDao.class);
	System.out.println(ud);//org.springframework.data.jpa.repository.support.SimpleJpaRepository@5fad41be
	System.out.println(ud.getClass());//class com.sun.proxy.$Proxy30

	}

从下面起 ,开始介绍SpringData Jpa的五个接口 ,使我们掌握其相关的用法 ,更加自如的进行数据库的crud操作

五、Repository接口

Repository 接口是Spring Data JPA 中为我们提供的所有接口中的顶层接口
Repository 提供了两种查询方式的支持
1)基于方法名称命名规则查询
2)基于@Query 注解查询

使用前提

  • dao层接口要继承Repository<实体类, 主键类型>接口
  • 导入 org.springframework.data.repository.Repository; //有重名jar

基于方法名称命名规则查询

规则 :findBy(关键字)+(属性名称的首字母大小写) +查询条件(首字母大写)

等值查询

接口层

/**
	 * 等值查询
	 * @param string
	 * @return
	 */
	List<Users> findByUsernameIs(String name);

测试类层

/**
	 * 需求 :使用用户名做查询条件
	 */
	@Test
	public void test1(){
		/**
		* 判断相等的条件,有三种表示方式 (  需要首先在接口中声明 )
		* 1  什么都不写,默认的就是做相等判断
		* 2 Is
		* 3 Equal
		*/
		List<Users> list = this.userDao.findByUsernameIs("宿州学院");
		for(Users users:list) {
			
			System.out.println(users);
		}
	}

查询结果
Spring全家桶之SpringData——Spring Data JPA_第6张图片

like查询

/**
	 * like关键字查询
	 * @param string
	 * @return
	 */
	List<Users> findByUsernameLike(String name);

测试类层

	/**
	* 需求:根据用户姓名做Like 处理
	* Like:条件关键字
	*/
	@Test
	public void test2() {
		
		List<Users> list = this.userDao.findByUsernameLike("宿州%");
		for(Users users:list) {
			
			System.out.println(users);			
		}
	}

查询结果
Spring全家桶之SpringData——Spring Data JPA_第7张图片

多条件查询

接口层

	/**
	 * 多条件查询 :名称等于并且年龄大于..
	 * @param name
	 * @param age
	 * @return
	 */
	List<Users> findByUsernameAndUserageGreaterThanEqual(String name,Integer age);

测试类层

/**
	* 需求:查询名称为宿州学院,并且他的年龄大于等于22 岁
	*/
	@Test
	public void test3() {
		
		List<Users> list = this.userDao.findByUsernameAndUserageGreaterThanEqual("宿州学院", 25);
		for(Users users:list) {
			
			System.out.println(users);			
		}
	}

查询结果

Spring全家桶之SpringData——Spring Data JPA_第8张图片

其他类型的查询

关键字 ———— 方法命名 ———— sql where子句

Spring全家桶之SpringData——Spring Data JPA_第9张图片
Spring全家桶之SpringData——Spring Data JPA_第10张图片

基于@query注解的查询

@Query 注解查询 , 根据这个注解改写上面的查询方法
名称书写无要求 ,本人这样写只是方便对比
接口层和查询结果层同上 ,故省略

等值查询

接口层

	@Query(value="from Users where username =?")
	List<Users> queryByUsername(String name);

like查询

接口层

	@Query("from Users where username like ?")
	List<Users> queryByUsernameLike(String keywords);

多条件查询

接口层

	@Query("from Users where username =? and userage >=?")
	List<Users> queryByUsernameAndUserageGreaterThanEqual(String username,Integer userage);

更新操作

@Modifying //标识当前语句是一个更新操作

接口层

/**
	 * @Modifying    //标识当前语句是一个更新操作
	 * @param userage
	 * @param userid
	 */
	@Query("update Users set userage =? where userid =?")
	@Modifying    
	void updateUserageByUserid(Integer userage,Integer userid);

六、CrudRepository接口

继承了该接口 ,无需实现类即可完成如下操作
支持增删改查及其批量处理

创建接口

package ah.szxy.dao;

import org.springframework.data.repository.CrudRepository;
import ah.szxy.pojo.Users;

public interface UserDao extends CrudRepository<Users, Integer>{
	
}

测试代码

更新数据方式二——在查询后直接修改 ,因为在查询后这个对象会处于持久化状态
所以需要开启需要关闭事务回滚

package ah.szxy.test;

import java.util.ArrayList;
import java.util.List;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;

import ah.szxy.dao.UserDao;
import ah.szxy.pojo.Users;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestCrudRepository {

	@Autowired
	private UserDao userDao;

	/**
	 * 插入数据
	 */
	@Test
	public void test1() {

		Users user = new Users();
		user.setUsername("两仪式");
		user.setUserage(18);
		this.userDao.save(user);
	}

	/**
	 * 批量插入数据
	 */
	@Test
	public void test2() {

		Users user = new Users();
		user.setUsername("阿尔利亚潘多拉贡");
		user.setUserage(16);

		Users user2 = new Users();
		user2.setUsername("卫宫士郎");
		user2.setUserage(18);

		ArrayList<Users> list = new ArrayList<Users>();
		list.add(user);
		list.add(user2);
		this.userDao.save(list);
	}

	/**
	 * 根据id查询单条数据
	 */
	@Test
	public void test3() {
		Users user = this.userDao.findOne(16);
		System.out.println(user);
	}

	/**
	 * 查询全部数据
	 * 主要对返回值做强转,因为返回的是Iterable
	 */
	@Test
	public void test4() {
		List<Users> list = (List<Users>) this.userDao.findAll();
		for (Users users : list) {
			System.out.println(users);
		}
	}

	/**
	 * 删除数据
	 */
	@Test
	public void test5() {
		this.userDao.delete(8);
	}

	/**
	 * 更新数据方式一
	 *先查询然后再插入
	 */
	@Test
	public void test6() {
		Users user = this.userDao.findOne(12);
		user.setUsername("王小红");
		this.userDao.save(user);
	}
	
	/**
	* 更新数据方式二
	* 在查询后直接修改 ,因为在查询后这个对象会处于持久化状态
	* 需要注意的是需要加上事务注解
	*/
	@Test
	@Transactional
	@Rollback(false)
	public void test7(){
		Users user = this.userDao.findOne(12);//持久化状态的
		user.setUsername("王小小");
		}
 	
}

七、PagingAndSortingRepository接口

见名知意 , 主要用于处理分页和排序

创建接口

public interface UserDao extends PagingAndSortingRepository<Users, Integer>{
	
}

测试代码

分页处理

注意导的包是否和如下一致!!!

package ah.szxy.test;

import java.util.List;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.domain.Sort.Order;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import ah.szxy.dao.UserDao;
import ah.szxy.pojo.Users;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestCrudRepository {

	@Autowired
	private UserDao userDao;

	/**
	 * 分页 page: 当前页的索引。注意索引都是从0 开始的。 size: 每页显示3 条数据
	 * 导入data.domain.*;
	 * PagingAndSortingRepository接口不能自己写查询条件进行分页 不过可以自己在定义一个dao接口以及实现。可以对自己的需求做扩展
	 */
	@Test
	public void test() {

		int page = 0;
		int size = 3;
		Pageable pageable = new PageRequest(page, size);
		Page<Users> p = this.userDao.findAll(pageable);
		System.out.println("数据的总条数 : " + p.getTotalElements());
		System.out.println("总页数 :" + p.getTotalPages());
		List<Users> list = p.getContent();
		for (Users users : list) {
			System.out.println(users);
		}
	}


排序处理

/**
	 * 对单列做排序处理
	 * 
	 * Sort:该对象封装了排序规则以及指定的排序字段(对象的属性来表示) direction:排序规则 properties:指定做排序的属性
	 */
	@Test
	public void test2() {

		Sort sort = new Sort(Direction.DESC, "userid");
		List<Users> list = (List<Users>) this.userDao.findAll(sort);
		for (Users users : list) {
			System.out.println(users);
		}
	}

	/**
	 * 多列的排序处理
	 */
	@Test
	public void test3() {
		// Sort:该对象封装了排序规则以及指定的排序字段(对象的属性来表示)
		// direction:排序规则
		// properties:指定做排序的属性
		Order order1 = new Order(Direction.DESC, "userid");
		Order order2 = new Order(Direction.ASC, "userage");
		Sort sort = new Sort(order1, order2);
		List<Users> list = (List<Users>) this.userDao.findAll(sort);
		for (Users users : list) {
			System.out.println(users);
		}
	}

八、JpaRepository 接口

JpaRepository 接口是我们开发时使用的最多的接口。其特点是可以帮助我们将其他接口的方法的返回值做适配处理。可以使得我们在开发时更方便的使用对数据库进行DML操作方法。

创建接口

public interface UserDao extends JpaRepository<Users, Integer>{
	
}

测试代码

无需强转 ,对比CrudRepository , 故一般会使用这个接口进行增删改查操作

package ah.szxy.test;

import java.util.List;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.domain.Sort.Order;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import ah.szxy.dao.UserDao;
import ah.szxy.pojo.Users;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestCrudRepository {

	@Autowired
	private UserDao userDao;
	
	/**
	 * 查询所有数据
	 * 无需强转 ,对比CrudRepository ,故一般会使用这个接口进行增删改查操作
	 */
	@Test
	public void test() {
		
		List<Users> list = this.userDao.findAll();
		for(Users users:list) {
			System.out.println(users);
		}
	}
}

九、JpaSpecificationExecutor接口

创建接口

JpaSpecificationExecutor:不能单独使用,需要配合 jpa 中的其他接口一起使用 , 可以完成多条件查询,并且支持带条件和排序的分页与查询

/**
 * JpaSpecificationExecutor 接口
 * 注意:JpaSpecificationExecutor:不能单独使用,需要配合着jpa 中的 其他接口一起使用
 * 
 * @author chy
 */
public interface UserDao extends JpaRepository<Users, Integer>, JpaSpecificationExecutor<Users> {

}

测试代码

单条件查询

package ah.szxy.test;

import java.util.ArrayList;
import java.util.List;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import ah.szxy.dao.UserDao;
import ah.szxy.pojo.Users;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestCrudRepository {

	@Autowired
	private UserDao userDao;

	/**
	 * 需求:根据用户姓名查询数据
	 */
	@Test
	public void test1() {
		//这里创建了一个匿名内部类,完成了查询条件的封装
		Specification<Users> spec = new Specification<Users>() {
			/**
			 * 那条件查询时还用or或者其他条件怎么办
			 * 使用什么条件可以直接使用CriteriaBuilder对象条用对应的方法
			 * @param  Root root:根对象。封装了查询条件的对象
			 * @param   CriteriaQuery query:定义了一个基本的查询.一般不使用
			 * @param CriteriaBuilder cb:创建一个查询条件
			 * @return Predicate:定义了查询条件
			 */
			@Override
			public Predicate toPredicate(Root<Users> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
				Predicate pre = cb.equal(root.get("username"), "卫宫士郎");
				return pre;
			}
		};
		List<Users> list = this.userDao.findAll(spec);
		for (Users users : list) {
			System.out.println(users);
		}
	}


}

多条件查询(and)

/**
	* 多条件查询方式一
	* 需求:使用用户姓名以及年龄查询数据
	*/
	@Test
	public void test2() {
		
		Specification<Users> spec = new Specification<Users>() {

			@Override
			public Predicate toPredicate(Root<Users> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
				//利用list封装查询条件
				ArrayList<Predicate> list = new ArrayList<Predicate>();
				list.add(cb.equal(root.get("username") , "卫宫士郎"));
				list.add(cb.equal(root.get("userage") , 18));
				//此时这两个条件没有关系 ,通过cd再为其加上关系
				Predicate[] arr=new Predicate[list.size()];
				return cb.and(list.toArray(arr));
			}
		};
		
		List<Users> users = this.userDao.findAll(spec);
		System.out.println(users);
	}
	

多条件查询(and/or另一种写法)

	/**
	* 多条件查询方式二
	* 需求:使用用户姓名或年龄查询数据
	*/
	@Test
	public void test3() {
		Specification<Users> spec = new Specification<Users>() {
			@Override
			public Predicate toPredicate(Root<Users> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
			
				return cb.or(cb.equal(root.get("username"), "宿州学院"),cb.equal(root.get("userage"), 23));
				
				//or 和and一起使用
//				return cb.and((cb.or(cb.equal(root.get("username"), "宿州学院"),cb.equal(root.get("userage"), 23)))
//						,cb.equal(root.get("userid"), 4));
			}
		};
		
		List<Users> users = this.userDao.findAll(spec);
		for (Users users2 : users) {
			System.out.println(users2);
		}
	}
	

带条件分页查询

/**
	 * 带条件的分页查询
	 * 需求:查询宿州姓用户,并且做分页处理
	 */
	@Test
	public void test4() {
		Specification<Users> spec = new Specification<Users>() {

			@Override
			public Predicate toPredicate(Root<Users> root, CriteriaQuery<?> query, CriteriaBuilder cb) {

				return cb.like(root.get("username").as(String.class ),"宿州%");
			}
		};
		
		Pageable pageable=new PageRequest(0, 2);
		Page<Users> page = this.userDao.findAll(spec, pageable);
		
		System.out.println("总条数 : "+page.getTotalElements());
		System.out.println("总页数 : "+page.getTotalPages());
		for (Users users : page) {
			System.out.println(users);
		}
	}

带条件的排序查询

/**
	 * 带条件的排序查询
	* 需求:查询数据库中存在宿州的用户,并且根据用户id 做倒序排序
	*/
	@Test
	public void test5() {
		Specification<Users> spec = new Specification<Users>() {

			@Override
			public Predicate toPredicate(Root<Users> root, CriteriaQuery<?> query, CriteriaBuilder cb) {

				return cb.like(root.get("username").as(String.class ),"宿州%");
			}
		};
		
		Sort sort=new Sort(Direction.DESC,"userage");
		List<Users> list = this.userDao.findAll(spec, sort);
		for (Users users : list) {
			System.out.println(users);
		}
	}
	

带条件与排序的分页查询

/**
	 * 带条件与排序的分页查询
	 * 使用的是分页查询的方法
	* 需求:查询数据库中存在宿州的用户,做分页处理,并且根据用户id 做倒序排序
	*/
	@Test
	public void Test6() {
		//1. 定义排序
		Sort sort=new Sort(Direction.DESC,"userid");
		Pageable pageable=new PageRequest(0, 2, sort);
		//2.定义分页
		Specification<Users> spec = new Specification<Users>() {
			@Override
			public Predicate toPredicate(Root<Users> root, CriteriaQuery<?> query, CriteriaBuilder cb) {			
				return cb.like(root.get("username").as(String.class), "宿州%");
			}
			
		};
		Page<Users> page = this.userDao.findAll(spec, pageable);
		System.out.println("总条数 : "+page.getTotalElements());
		System.out.println("总页数 : "+page.getTotalPages());
		for (Users users : page) {
			System.out.println(users);
		}
	}

十、用户自定义Repository接口

用户通过自定义接口来定义一些业务逻辑,通过接口的实现类实现,
然后利用Dao层接口继承我们所编写的自定义Repository接口.
测试类可以通过注入Dao层接口来调用我们在自定义接口中定义的方法

创建接口

package ah.szxy.dao;

import ah.szxy.pojo.Users;

public interface UserSelfRepository {
	
	public Users findUserById(Integer userid);
}

创建实现类

package ah.szxy.dao;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import ah.szxy.pojo.Users;

public class UserDaoImpl implements UserSelfRepository {
	
	@PersistenceContext(name="entityManagerFactory")
	private EntityManager em;
	
	@Override
	public Users findUserById(Integer userid) {

		System.out.println("MyRepository......");
		return this.em.find(Users.class, userid);
	}

}

使用接口

package ah.szxy.dao;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;

import ah.szxy.pojo.Users;

/**
 * 用户自定义接口实现
 * 需要在这里继承自己编写好的接口
 * @author chy
 */
public interface UserDao extends JpaRepository<Users,Integer>,
			JpaSpecificationExecutor<Users>,UserSelfRepository{

}

编写测试代码

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class UserSelfRepository {

	@Autowired
	private UserDao userDao;

	/**
	 * 根据用户id查询用户信息
	 */
	@Test
	public void test1() {
	
		Users user = this.userDao.findUserById(9);
		System.out.println(user);
	}	
}

十一、关系映射操作

一对一的关联操作

需求:用户与角色的一对一的关联关系
用户:一方
角色:一方

创建用户实体

添加用于关联的引用对象的属性 ,并添加相应的取值赋值方法

package ah.szxy.pojo;

import java.io.Serializable;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
import javax.persistence.Table;

@Entity
@Table(name="t_users")
public class Users implements Serializable{
	//依次为主键 ,自增长 ,列名
	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	@Column(name="userid")
	private Integer userid;
	
	@Column(name="username")
	private String username;
	
	@Column(name="userage")
	private Integer userage;

	@OneToOne(cascade=CascadeType.PERSIST)  //创建级联操作
	@JoinColumn(name="roles_id") //创建并维护这个外键
	private Roles roles;
	
	
	
	
	public Roles getRoles() {
		return roles;
	}

	public void setRoles(Roles roles) {
		this.roles = roles;
	}

	public Integer getUserid() {
		return userid;
	}

	public void setUserid(Integer userid) {
		this.userid = userid;
	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public Integer getUserage() {
		return userage;
	}

	public void setUserage(Integer userage) {
		this.userage = userage;
	}

	@Override
	public String toString() {
		return "Users [userid=" + userid + ", username=" + username + ", userage=" + userage + "]";
	}

	public Users(Integer userid, String username, Integer userage) {
		super();
		this.userid = userid;
		this.username = username;
		this.userage = userage;
	}

	public Users() {
		super();
	}
	
	
}

创建角色实体

添加用于关联的引用对象的属性 ,并添加相应的取值赋值方法

@Entity
@Table(name="t_roles")  //表示开启正向工程,运行后会自动常见t_roles这个表
public class Roles {
	
	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)  //自增长
	@Column
	private Integer roleId;
	@Column
	private String roleName;
	
	@OneToOne(mappedBy="roles")
	private Users users;
	
	
	
	public Users getUsers() {
		return users;
	}
	public void setUsers(Users users) {
		this.users = users;
	}
	public Integer getRoleId() {
		return roleId;
	}
	public void setRoleId(Integer roleId) {
		this.roleId = roleId;
	}
	public String getRoleName() {
		return roleName;
	}
	public void setRoleName(String roleName) {
		this.roleName = roleName;
	}
	@Override
	public String toString() {
		return "Roles [roleId=" + roleId + ", roleName=" + roleName + "]";
	}
	public Roles(Integer roleId, String roleName) {
		super();
		this.roleId = roleId;
		this.roleName = roleName;
	}
	public Roles() {
		super();
	}
	
	
	
}

测试代码

package ah.szxy.test;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import ah.szxy.dao.UserDao;
import ah.szxy.pojo.Roles;
import ah.szxy.pojo.Users;

/**
 * 一对一关系测试
 * 
 * @author chy
 *
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestOneToOne {

	@Autowired
	private UserDao userDao;

	@Test
	public void test1() {
		// 创建角色
		Roles roles = new Roles();
		roles.setRoleName("管理员");
		// 创建用户
		Users users = new Users();
		users.setUserage(37);
		users.setUsername("赵小雷");
		// 建立关系
		users.setRoles(roles);
		roles.setUsers(users);
		// 保存数据
		this.userDao.save(users);
	}

	/**
	 * 根据用户ID 查询用户,同时查询用户角色
	 */
	@Test
	public void test2() {
		Users users = this.userDao.findOne(20);
		System.out.println("用户信息:" + users);
		Roles roles = users.getRoles();
		System.out.println(roles);
	}
}

运行结果

Spring全家桶之SpringData——Spring Data JPA_第11张图片

一对多的关联操作

需求:从角色到用户的一对多的关联关系
角色:一方
用户:多方(添加外键)

创建用户实体

需要在添加外键的那一开启级联操作 ,防止数据插入时出现异常
@ManyToOne(cascade=CascadeType.PERSIST)
@JoinColumn(name=“roles_id”) //创个栏位开启外键并维护这个外键

package ah.szxy.pojo;

import java.io.Serializable;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToOne;
import javax.persistence.Table;

@Entity
@Table(name="t_users")
public class Users implements Serializable{
	//依次为主键 ,自增长 ,列名
	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	@Column(name="userid")
	private Integer userid;
	
	@Column(name="username")
	private String username;
	
	@Column(name="userage")
	private Integer userage;

	@ManyToOne(cascade=CascadeType.PERSIST)
	@JoinColumn(name="roles_id") //创个栏位开启外键并维护这个外键
	private Roles roles;
	 
	
//其他方法省略
}

创建角色实体

注意在toString() 不能打印Users的值, 因为在测试方法中 Roles的值本来就是通过Users获取的,
//但是我们又无法通过Roles 获取Users 因为他们不是多对多的关系

用户表中添加了 @ManyToOne()
角色表中添加了 @OneToMany()

package ah.szxy.pojo;

import java.util.HashSet;
import java.util.Set;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.Table;

@Entity
@Table(name="t_roles")  //表示开启正向工程,运行后会自动常见t_roles这个表
public class Roles {
	
	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)  //自增长
	@Column
	private Integer roleId;
	@Column
	private String roleName;
	
	
	@OneToMany(mappedBy="roles")
	private Set<Users> users=new HashSet<Users>();//用set集合来接收外键和主键相同的多个用户
	
	
	//其他方法省略 ,注意在toString() 不能打印Users的值,因为在测试方法中 Roles的值本来就是通过Users获取的,
	//但是我们又无法通过Roles 获取Users 因为他们不是多对多的关系
	
}

测试代码

/**
 * 测试一对多关系
 * 
 * @author chy
 *
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestOneToMany {

	@Autowired
	private UserDao userDao;

	/**
	 * 添加用户同时添加角色
	 */
	@Test
	public void test1() {
		// 创建角色
		Roles roles = new Roles();
		roles.setRoleName("战斗天使");
		// 创建用户
		Users users = new Users();
		users.setUsername("阿丽塔");
		users.setUserage(24);
		// 建立关系
		roles.getUsers().add(users); // 为users这个set集合添加属性
		users.setRoles(roles); // 为user保存roles相关信息 ,如外键roles_id
		// 保存数据
		this.userDao.save(users);
	}

	/**
	 * 根据用户ID 查询用户信息,同时查询角色
	 */
	@Test
	public void test2() {
		Users users = this.userDao.findOne(23);
		System.out.println("用户姓名:" + users.getUsername());
		Roles roles = users.getRoles();
		System.out.println(roles);
	}

}

多对多的关联操作

需求:一个角色可以拥有多个菜单,一个菜单可以分配多个角色。多对多的关联关系
角色:多方(哪一方都可以创建外键 ,先在这里创建外键)
菜单:多方

创建菜单实体

@Entity
@Table(name="t_menus")
public class Menus {
	
	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	@Column(name="menuId")
	private Integer menuId;
	@Column(name="menuName")
	private String menuName;
	@Column(name="menuUrl")
	private String menuUrl;
	@Column(name="fatherId")
	private Integer fatherId;
	
	@ManyToMany(mappedBy="menus",fetch=FetchType.EAGER)
	private Set<Roles> roles = new HashSet<>();

//其他方法省略
	
}

创建角色实体

@ManyToMany(cascade=CascadeType.PERSIST,fetch=FetchType.EAGER)

  • FetchType.EAGER 放弃延迟加载,解决多对多查询时,查询闻不到对象的问题 ,最好两边都要加上这个属性

@JoinTable(name=“t_roles_menus”,joinColumns=@JoinColumn(name=“role_id”),inverseJoinColumns=@JoinColumn(name=“menu_id”))

  • joinColumns:建立当前表在中间表中的外键字段
  • inverseJoinColumns :建立另一张表在中间表中的外键字段
@Entity
@Table(name="t_roles")  //表示开启正向工程,运行后会自动常见t_roles这个表
public class Roles {
	
	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)  //自增长
	@Column
	private Integer roleId;
	@Column
	private String roleName;
	
	//FetchType 放弃延迟加载,解决多对多查询时,查询闻不到对象的问题
	@ManyToMany(cascade=CascadeType.PERSIST,fetch=FetchType.EAGER)
	//@JoinTable:配置中间表信息
	//joinColumns:建立当前表在中间表中的外键字段
	//inverseJoinColumns :建立另一张表在中间表中的外键字段
	@JoinTable(name="t_roles_menus",joinColumns=@JoinColumn(name="role_id"),
		inverseJoinColumns=@JoinColumn(name="menu_id"))
	private Set<Menus> menus = new HashSet<>();
	
	//其他方法省略
	
}

创建接口

/**
 * 多对多关系的实现
 * @author chy
 *
 */
public interface RoleDao extends JpaRepository<Roles, Integer> {

}

测试代码

/**
 * 测试多对多关联操作
 * @author chy
 *
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestManyToMany {

	@Autowired
	private RoleDao roleDao;
	
	/**
	* 添加角色同时添加菜单
	*/
	@Test
	public void test1() {
		// 创建角色
		Roles roles = new Roles();
		roles.setRoleName("超管");
		// 创建菜单
		Menus menus = new Menus();
		menus.setFatherId(-1);
		menus.setMenuName("CSND博客平台");
		menus.setMenuUrl("www.csdn.com");

		Menus menus2 = new Menus();
		menus2.setFatherId(1);
		menus2.setMenuName("博客园博客平台");
		menus2.setMenuUrl("www.cnblogs.com");

		// 添加关系
		roles.getMenus().add(menus);
		roles.getMenus().add(menus2);

		menus.getRoles().add(roles);
		menus2.getRoles().add(roles);
		// 保存数据
		this.roleDao.save(roles);
	}

	/**
	 * 查询Roles
	 */
	@Test
	public void test2() {
		Roles roles = this.roleDao.findOne(3);
		System.out.println("角色信息:" + roles);
		Set<Menus> menus = roles.getMenus();
		for (Menus menus2 : menus) {
			System.out.println("菜单信息:" + menus2);
		}
	}
}

测试结果
Spring全家桶之SpringData——Spring Data JPA_第12张图片

你可能感兴趣的:(SpringData,Jpa,正向工程,JAVA小窝(笔记),Spring技术)