SpringDataJpa
1.ORM思想
将对象与数据库表建立一种映射关系,这样就可以通过操作对象的方式实现对数据库表的操作
1.1 映射关系
Java类==表
类的属性==表的字段
类的对象==表的数据行
2. JPA与Hibernate介绍
JPA是SUN针对ORM映射的标准和规范
Hibernate是ORM的一种实现框架
使用JPA的特点
1. 面向接口编程,标准化开发
2. 配置简单,易用
3. 提供了面向对象的查询语言,类似于SQL语法,易于查询
3.编写核心配置文件
3.1文件位置
META-INF/persistence.xml
3.2文件内容
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
<!--transaction-type:RESOURCE_LOCAL 本地事务; JTA:分布式事务 -->
<persistence-unit name="myJpa" transaction-type="RESOURCE_LOCAL">
<!--jpa的实现方式 -->
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<properties>
<property name="javax.persistence.jdbc.user" value="root"/>
<property name="javax.persistence.jdbc.password" value="root"/>
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql:///jpa"/>
<!--是否打印SQL-->
<property name="hibernate.show_sql" value="true" />
<!--是否格式化SQL-->
<property name="hibernate.format_sql" value="true" />
<!--DDL语句设置-->
<property name="hibernate.hbm2ddl.auto" value="update" />
<!--
hbm2ddl取值:
create: 程序运行时创建数据库表(如果有表,先删除表再创建)
update:程序运行时创建表(如果有表,不会创建表)
none:不会创建表
-->
</properties>
</persistence-unit>
</persistence>
3.3主键生成策略(GeneratedValue)
SEQUENCE:序列(Oracle数据库用)
IDENTITY:数据库自增(Mysql数据库用)
TABLE:Hibernate负责生成主键值,并自动创建一个序列表,存储实体类对应表中的主键值.
AUTO:Hibernate自动选择主键生成策略(优先选择使用SEQUENCE)
4.JPA常用API介绍
4.1 Persistence
加载配置文件,创建EntityManagerFactory对象
4.2 EntityManagerFactory
创建EntityManager对象,并且是线程安全对象,全局共享一个即可
4.3 EntityManager
CURD操作的核心对象
getTransaction : 获取事务对象
persist : 保存操作
merge : 更新操作
remove : 删除操作
find/getReference : 根据id查询
4.4 EntityTransaction
操作事务的对象
begin:开启事务
commit:提交事务
rollback:回滚事务
5.JPA增删改查
5.1JPA工具类
/**
* JPA工具类,用来获得EntityManager对象
*/
public class JpaUtils {
private static EntityManagerFactory factory;
static {
factory = Persistence.createEntityManagerFactory("myJpa");
}
public static EntityManager getEntityManager() {
return factory.createEntityManager();
}
}
5.2根据ID查询
find和getReference的的异同
- 相同点
都可以根据ID查询对象数据 - 不同点
find方法立即加载,getReference懒加载
find方法返回实体类对象,getReference返回代理对象
如果未查询到数据,find方法返回null,getReference会报错.
5.3根据ID删除对象
remove
5.4根据ID更新对象
merge
6. SpringDataJPA概述
SpringDataJPA是Spring针对JPA进行封装的框架,目的是简化JPA针对DB持久层的操作
6.1配置文件
applicationContext.xml
<!-- 1.创建entityManagerFactory对象交给spring容器管理-->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<!--配置的扫描的包(实体类所在的包) -->
<property name="packagesToScan" value="cn.itcast.domain" />
<!-- jpa的实现厂家 -->
<property name="persistenceProvider">
<bean class="org.hibernate.jpa.HibernatePersistenceProvider"/>
</property>
<!--jpa的供应商适配器 -->
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<!--配置是否自动创建数据库表 -->
<property name="generateDdl" value="true" />
<!--指定数据库类型 -->
<property name="database" value="MYSQL" />
<!--数据库方言:支持的不同数据库sql特有语法 -->
<property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect" />
<!--是否显示sql -->
<property name="showSql" value="true" />
</bean>
</property>
<!--jpa的方言 :JPA实现厂商高级的特性 -->
<property name="jpaDialect" >
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
</property>
</bean>
<!--2.创建数据库连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="root"></property>
<property name="password" value="root"></property>
<property name="jdbcUrl" value="jdbc:mysql:///jpa" ></property>
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
</bean>
<!--3.整合spring dataJpa-->
<jpa:repositories base-package="cn.itcast.dao" entity-manager-factory-ref="entityManagerFactoty" />
<!--4.配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactoty"></property>
</bean>
<!-- 5. 配置包扫描-->
<context:component-scan base-package="cn.corn"/>
6.2增删改查
增:save
删:delete
改:save
查:findOne
getOne:需要Transactional
查询所有:findAll
判断某条数据是否存在:exists
统计查询:count
NO. | 关键词 | 实例 | 同功能JPQL |
1 | And | findByLastnameAndFirstname | … where x.lastname = ?1 and x.firstname = ?2 |
2 | Or | findByLastnameOrFirstname | … where x.lastname = ?1 or x.firstname = ?2 |
3 | Is,Equals | findByFirstnameIs,findByFirstnameEquals | … where x.firstname = ?1 |
4 | Between | findByStartDateBetween | |
5 | LessThan | findByAgeLessThan | … where x.age < ?1 |
6 | LessThanEqual | findByAgeLessThanEqual | … where x.age ⇐ ?1 |
7 | GreaterThan | findByAgeGreaterThan | … where x.age > ?1 |
8 | GreaterThanEqual | findByAgeGreaterThanEqual | … where x.age >= ?1 |
9 | After | findByStartDateAfter | … where x.startDate > ?1 |
10 | Before | findByStartDateBefore | … where x.startDate < ?1 |
11 | IsNull | findByAgeIsNull | … where x.age is null |
12 | IsNotNull,NotNull | findByAge(Is)NotNull | … where x.age not null |
13 | Like | findByFirstnameLike | … where x.firstname like ?1 |
14 | NotLike | findByFirstnameNotLike | … where x.firstname not like ?1 |
15 | StartingWith | findByFirstnameStartingWith | … where x.firstname like ?1 (parameter bound with appended %) |
16 | EndingWith | findByFirstnameEndingWith | … where x.firstname like ?1 (parameter bound with prepended %) |
17 | Containing | findByFirstnameContaining | … where x.firstname like ?1 (parameter bound wrapped in %) |
18 | OrderBy | findByAgeOrderByLastnameDesc | … where x.age = ?1 order by x.lastname desc |
19 | Not | findByLastnameNot | … where x.lastname <> ?1 |
20 | In | findByAgeIn(Collection ages) | … where x.age in ?1 |
21 | NotIn | findByAgeNotIn(Collection age) | … where x.age not in ?1 |
22 | TRUE | findByActiveTrue() | … where x.active = true |
23 | FALSE | findByActiveFalse() | … where x.active = false |
24 | IgnoreCase | findByFirstnameIgnoreCase | … where UPPER(x.firstame) = UPPER(?1) |
7.Specification动态条件查询
7.1 实现步骤
1. Dao接口继承JpaSpecificationExecutor
2. 创建Specification对象封装查询条件
3. 调用JpaSpecificationExecutor接口方法,传递Specification对象进行条件查询
7.2案例
7.2.1 单条件查询
@Test
public void testSpec() {
Specification<Customer> spec = new Specification<Customer>() {
public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
//1.获得条件查询属性
Path<Object> custName = root.get("custName");
//2.构造查询条件;
//同功能JPQL: from Customer where custName = "参数"
Predicate predicate = cb.equal(custName, "参数");
return predicate;
}
};
Customer customer = customerDao.findOne(spec);
System.out.println(customer);
}
7.2.2 多条件
@Test
public void testSpec1() {
Specification<Customer> spec = new Specification<Customer>() {
public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
//属性1:客户名
Path<Object> custName = root.get("custName");
//属性2:所属行业
Path<Object> custIndustry = root.get("custIndustry");
//构造查询条件
//1.构造客户名的精准匹配查询
Predicate p1 = cb.equal(custName, "传参数");
//2.构造所属行业的精准匹配查询
Predicate p2 = cb.equal(custIndustry, "参数");
//3.将多个查询条件进行组合
//同功能JPQL语句:from Customer where custName = "参数" and custIndustry="参数"
Predicate and = cb.and(p1, p2);
return and;
}
};
Customer customer = customerDao.findOne(spec);
}
7.2.3 带条件查询并排序
@Test
public void testSpec3() {
Specification<Customer> spec = new Specification<Customer>() {
public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
//查询属性:客户名
Path<Object> custName = root.get("custName");
//查询方式:模糊匹配,除过equals的其他查询方式必须指定比较的属性类型
Predicate like = cb.like(custName.as(String.class), "参数%");
return like;
}
};
//创建排序对象
Sort sort = new Sort(Sort.Direction.DESC,"custId");
//执行查询
List<Customer> list = customerDao.findAll(spec, sort);
for (Customer customer : list) {
System.out.println(customer);
}
}
7.2.4 带条件分页查询
@Test
public void testSpec4() {
//条件条件对象
Specification spec = new Specification() {
public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) {
//查询属性:客户名
Path<Object> custName = root.get("custName");
//查询方式:模糊匹配,除过equals的其他查询方式必须指定比较的属性类型
Predicate like = cb.like(custName.as(String.class), "参数%");
return like;
}
};
//创建分页对象,参数1:从哪里开始查;参数2:查询多少条
Pageable pageable = new PageRequest(0,5);
//分页查询
Page<Customer> page = customerDao.findAll(spec, pageable);
}
7.2.5 动态查询
public void test(){
Customer customer = new Customer();
customer.setCustName("参数");
Specification<Customer> spec = new Specification<Customer>() {
@Override
public Predicate toPredicate(Root<Customer> root,CriteriaQuery<?> query, CriteriaBuilder cb){
List<Predicate> list = new ArrayList<Predicate>();
//判断cusName是否有值
if(customer.getCustName()!=null && !"".equals(customer.getCustName())){
Path<Object> cusName = root.get("custName");
Predicate p1 = cb.like(cusName.as(String.class), customer.getCustName());
list.add(p1);
}
//判断custIndustry是否有值
if(customer.getCustIndustry()!=null && !"".equals(customer.getCustIndustry())){
Path<Object> custIndustry = root.get("custIndustry");
Predicate p2 = cb.equal(custIndustry.as(String.class), customer.getCustIndustry());
list.add(p2);
}
Predicate[] predicates = list.toArray(new Predicate[0]);
return cb.and(predicates);
}
};
List<Customer> list = customerDao.findAll(spec);
}
7.3 cb查询方式总结
方法名称 | Sql对应关系 |
equle | filed = value |
gt(greaterThan ) | filed > value |
lt(lessThan ) | filed < value |
ge(greaterThanOrEqualTo ) | filed >= value |
le( lessThanOrEqualTo) | filed <= value |
notEqule | filed != value |
like | filed like value |
notLike | filed not like value |
8.JPA多表查询
8.1 JPA实体的实现
一对一:在任意一方添加对方的对象属性
一对多:在一方添加多方的集合属性,在多方中添加一方的对象属性
多对多:在任意一方添加对方的集合属性
8.2ORM配置多表关系步骤
1. 添加关系注解:@OneToMany @ManyToOne @ManyToMany
2. 确定由哪方维护外键关系(另外一方放弃关系维护)
3. 在维护关系一方配置外键信息
8.3一对多配置
一方
@Entity
@Table(name="cst_customer")
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long custId;
...
private String custSource;
//一对多注解,一方放弃维护外键关系
@OneToMany(mappedBy = "customer")
private Set<LinkMan> linkMans = new HashSet<LinkMan>();
}
多方
@Entity
@Table(name = "cst_linkman")
public class LinkMan {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long lkmId; //联系人编号(主键)
...
private String lkmMemo;//联系人备注
//多对一注解,多方维护外键关系,配置外键名称
@ManyToOne
@JoinColumn(name = "lkm_cust_id")
private Customer customer;
}
8.3.1 一般保存
@Test
@Transactional //配置事务
@Rollback(false) //不自动回滚
public void testAdd() {
//创建一个客户
Customer customer = new Customer();
customer.setCustName("百度");
//创建一个联系人
LinkMan linkMan = new LinkMan();
linkMan.setLkmName("小李");
//维护外键关系
linkMan.setCustomer(customer);
//保存客户
customerDao.save(customer);
//保存联系人
linkManDao.save(linkMan);
}
8.3.2 级联保存
//级联添加:保存一个客户的同时,保存客户的所有联系人
@Test
@Transactional //配置事务
@Rollback(false) //不自动回滚
public void testCascadeAdd() {
Customer customer = new Customer();
customer.setCustName("百度1");
LinkMan linkMan = new LinkMan();
linkMan.setLkmName("小李1");
//维护外键
linkMan.setCustomer(customer);
//级联保存
customer.getLinkMans().add(linkMan);
customerDao.save(customer);
}
8.3.3 级联删除
//级联删除:删除客户的同时,删除该客户的所有联系人
@Test
@Transactional //配置事务
@Rollback(false) //不自动回滚
public void testCascadeRemove() {
//1.查询1号客户
Customer customer = customerDao.findOne(1L);
//2.删除1号客户
customerDao.delete(customer);
}
8.4 多对多配置
多方
@Entity
@Table(name = "sys_user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long userId;
private String userName;
private Integer age;
//配置多对多关系,User方维护外键关系
@ManyToMany
@JoinTable(name="user_role",joinColumns = {@JoinColumn(name ="user_id" )},
inverseJoinColumns = {@JoinColumn(name="role_id")})
private Set<Role> roles = new HashSet<Role>();
}
另一多方
@Entity
@Table(name = "sys_role")
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long roleId;
private String roleName;
//配置多对多关系,Role方放弃维护外键关系
@ManyToMany(mappedBy = "roles") //配置多表关系
private Set<User> users = new HashSet<User>();
}
8.4.1 一般保存
// 保存一个用户,保存一个角色
@Test
@Transactional
@Rollback(false)
public void testAdd() {
User user = new User();
user.setUserName("小李");
Role role = new Role();
role.setRoleName("java");
//用户维护外键关系
user.getRoles().add(role);
userDao.save(user);
roleDao.save(role);
}
8.4.2 级联保存
//测试级联添加(保存一个用户的同时保存用户的关联角色)
@Test
@Transactional
@Rollback(false)
public void testCasCadeAdd() {
User user = new User();
user.setUserName("小李");
Role role = new Role();
role.setRoleName("java");
//配置用户到角色关系,可以对中间表中的数据进行维护 1-1
user.getRoles().add(role);
//保存客户,级联保存角色
userDao.save(user);
}
8.4.3 级联删除
//案例:删除id为5的用户,同时删除他的关联角色
@Test
@Transactional
@Rollback(false)
public void testCasCadeRemove() {
//查询5号用户
User user = userDao.findOne(5L);
//删除5号用户
userDao.delete(user);
}
8.5. 级联配置总结
//在关系注解上添加如下属性
增:CascadeType.PERSIST
删:CascadeType.REMOVE
改:CascadeType.MERGE
所有:CascadeType.ALL
9. 多表之间的查询
9.1 对象导航图查询
通过实体类中的关联属性查询关联数据
一方查询多方
@Test
@Transactional // 解决在java代码中的no session问题
public void testQuery1() {
//查询id为1的客户
Customer customer = customerDao.getOne(6L);
//对象导航查询,此客户下的所有联系人
Set<LinkMan> linkMans = customer.getLinkMans();
}
多方查询一方
@Test
public void testQuery3() {
LinkMan linkMan = linkManDao.findOne(6L);
//对象导航查询所属的客户
Customer customer = linkMan.getCustomer();
}
一方查询多方,默认是懒加载
多方查询一方,默认是立即加载
配置对象导航图加载方式
//在关系注解上添加如下属性
立即加载:fetch=FetchType.EAGER
懒加载:fetch=FetchType.LAZY
9.2 Specification查询
@Test
public void testFind() {
Specification<LinkMan> spec = new Specification<LinkMan>() {
public Predicate toPredicate(Root<LinkMan> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
/**
* Join代表连接查询,通过root对象获取
* JoinType.INNER
* JoinType.LEFT
* JoinType.RIGHT
*/
Join<LinkMan, Customer> join = root.join("customer", JoinType.INNER);
return cb.like(join.get("custName").as(String.class),"百度");
}
};
List<LinkMan> list = linkManDao.findAll(spec);
}