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 实现无需在dao层实现类书写代码即可实现对数据库的操作
使用的查询语言是 HQL语言
本项目是基于 Hibernate Jpa项目上构建 ,Hibernate Jpa查看上篇博文
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
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
<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();
}
}
JpaRepository
: 接口中没有方法 ,没有接口实现类 ,接口内置
因为是自动生成的接口以及实现类 ,所以只能进行简单的数据库操作
可以调用的超类的方法如下
接口类
package ah.szxy.dao;
import org.springframework.data.jpa.repository.JpaRepository;
import ah.szxy.pojo.Users;
// JpaRepository<实体类, 主键类型>
public interface UserDao extends JpaRepository<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.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);
}
}
通过运行测试类 ,来简单的帮我们理解一下其运行原理
@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 接口是Spring Data JPA 中为我们提供的所有接口中的顶层接口
Repository 提供了两种查询方式的支持
1)基于方法名称命名规则查询
2)基于@Query 注解查询
使用前提
规则 :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);
}
}
/**
* 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);
}
}
接口层
/**
* 多条件查询 :名称等于并且年龄大于..
* @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);
}
}
查询结果
关键字 ———— 方法命名 ———— sql where子句
@Query 注解查询 , 根据这个注解改写上面的查询方法
名称书写无要求 ,本人这样写只是方便对比
接口层和查询结果层同上 ,故省略
接口层
@Query(value="from Users where username =?")
List<Users> queryByUsername(String name);
接口层
@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);
继承了该接口 ,无需实现类即可完成如下操作
支持增删改查及其批量处理
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("王小小");
}
}
见名知意 , 主要用于处理分页和排序
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 接口是我们开发时使用的最多的接口。其特点是可以帮助我们将其他接口的方法的返回值做适配处理。可以使得我们在开发时更方便的使用对数据库进行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:不能单独使用,需要配合 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);
}
}
}
/**
* 多条件查询方式一
* 需求:使用用户姓名以及年龄查询数据
*/
@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);
}
/**
* 多条件查询方式二
* 需求:使用用户姓名或年龄查询数据
*/
@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);
}
}
用户通过自定义接口来定义一些业务逻辑,通过接口的实现类实现,
然后利用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);
}
}
运行结果
需求:从角色到用户的一对多的关联关系
角色:一方
用户:多方(添加外键)
需要在添加外键的那一开启级联操作 ,防止数据插入时出现异常
@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)
@JoinTable(name=“t_roles_menus”,joinColumns=@JoinColumn(name=“role_id”),inverseJoinColumns=@JoinColumn(name=“menu_id”))
@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);
}
}
}