文章目录
1、SpringDataJPA
JPA是Java EE 5规范中提出的Java持久化接口,Hibernate是一个ORM框架,Spring Data JPA是基于JPA规范的,而Hibernate是一个ORM框架,它实现了JPA规范。因此,Spring Data JPA是基于Hibernate的,但是它提供了更多的功能和更好的性能 。
2、配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:jpa="http://www.springframework.org/schema/data/jpa" xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/data/jpa
http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">
<!--spring 和 spring data jpa的配置-->
<!-- 1.创建entityManagerFactory对象交给spring容器管理-->
<bean id="entityManagerFactoty" 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="false" />
<!--指定数据库类型 -->
<property name="database" value="MYSQL" />
<!--数据库方言:支持的特有语法 -->
<property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect" />
<!--是否显示sql -->
<property name="showSql" value="true" />
</bean>
</property>
<!--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="sys as sysdba"></property>
<property name="password" value="root"></property>
<property name="jdbcUrl" value="jdbc:oracle:thin:@localhost:1521/orcl" ></property>
<property name="driverClass" value="oracle.jdbc.OracleDriver"></property>
</bean>
<!--3.整合spring dataJpa-->
<jpa:repositories base-package="cn.itcast.dao" transaction-manager-ref="transactionManager"
entity-manager-factory-ref="entityManagerFactoty" ></jpa:repositories>
<!--4.配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactoty"></property>
</bean>
<!-- 4.txAdvice-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="insert*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="delete*" propagation="REQUIRED"/>
<tx:method name="get*" read-only="true"/>
<tx:method name="find*" read-only="true"/>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!-- 5.aop-->
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* cn.itcast.service.*.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut" />
</aop:config>
<!--5.声明式事务 -->
<!-- 6. 配置包扫描,扫描包下的所有注解-->
<context:component-scan base-package="cn.itcast" ></context:component-scan>
</beans>
3、实体类
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
/**
* 1.实体类和表的映射关系
* @Eitity 声明实体类
* @Table 指定数据库表
* 2.类中属性和表中字段的映射关系
* @Id 对应的数据库表的主键
* @GeneratedValue 对应的数据库的主键生成策略,不一样的数据库,生成策略不一样
* @Column 对应的数据库表字段
*
* * 所有的注解都是使用JPA的规范提供的注解,
* * 所以在导入注解包的时候,一定要导入javax.persistence下的
*/
@Entity //声明实体类
@Table(name="cst_customer") //建立实体类和表的映射关系
public class Customer {
@Id//声明当前私有属性为主键
@GeneratedValue(strategy=GenerationType.SEQUENCE) //配置主键的生成策略
@Column(name="cust_id") //指定和表中cust_id字段的映射关系
private Long custId;
//省略get\set
}
4、Dao
4.1 jpql和sql查询
dao继承JpaRepository
和JpaSpecificationExecutor
,
继承了JpaRepository
,就可以实现基本的操作,
继承了JpaSpecificationExecutor
,就可以实现复杂查询(分页),
继承了这两个接口不满足需求的话,可以自己写方法,通过@Query
来写数据库操作的语句
@Query
查询语句分为jpql
语句和sql
语句,使用sql
要在@Query
中使用nativeQuery = true
jpql
:语句中的对象和属性都是实体类中的类名和属性名
sql
:语句中的对象和属性都是数据库表中的表名和字段名
import cn.itcast.domain.Customer;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import java.util.List;
/**
* 符合SpringDataJpa的dao层接口规范
* JpaRepository<操作的实体类类型,实体类中主键属性的类型>
* * 封装了基本CRUD操作
* JpaSpecificationExecutor<操作的实体类类型>
* * 封装了复杂查询(分页)
*/
public interface CustomerDao extends JpaRepository<Customer,Long> ,JpaSpecificationExecutor<Customer> {
/**
* 案例:根据客户名称查询客户
* 使用jpql的形式查询
* jpql:from Customer where custName = ?
*
* 配置jpql语句,使用的@Query注解
*/
@Query(value="from Customer where custName = ?1")
public Customer findJpql(String custName);
/**
* 案例:根据客户名称和客户id查询客户
* jpql: from Customer where custName = ? and custId = ?
*
* 对于多个占位符参数
* 赋值的时候,默认的情况下,占位符的位置需要和方法参数中的位置保持一致
*
* 可以指定占位符参数的位置
* ? 索引的方式,指定此占位的取值来源
*/
@Query(value = "from Customer where custName = ?2 and custId = ?1")
public Customer findCustNameAndId(Long id,String name);
/**
* 使用jpql完成更新操作
* 案例 : 根据id更新,客户的名称
* 更新4号客户的名称,将名称改为“程序员”
*
* sql :update cst_customer set cust_name = ? where cust_id = ?
* jpql : update Customer set custName = ? where custId = ?
*
* @Query : 代表的是进行查询
* * 声明此方法是用来进行更新操作
* @Modifying
* * 当前执行的是一个更新操作
*
*/
@Query(value = " update Customer set custName = ?2 where custId = ?1 ")
@Modifying
public void updateCustomer(long custId,String custName);
/**
* 使用sql的形式查询:
* 查询全部的客户
* sql : select * from cst_customer;
* Query : 配置sql查询
* value : sql语句
* nativeQuery : 查询方式
* true : sql查询
* false:jpql查询
*
*/ /**
* Method to deserialize JSON content as tree expressed using set of {@link JsonNode} instances.
* Returns root of the resulting tree (where root can consist of just a single node if the current
* event is a value event, not container).
*
* @param file File of which contents to parse as JSON for building a tree instance
*
* @since 1.9
*/
//@Query(value = " select * from cst_customer" ,nativeQuery = true)
@Query(value="select * from cst_customer where cust_name like ?1",nativeQuery = true)
public List<Object [] > findSql(String name);
/**
* 方法名的约定:
* findBy : 查询
* 对象中的属性名(首字母大写) : 查询的条件
* CustName
* * 默认情况 : 使用 等于的方式查询
* 特殊的查询方式
*
* findByCustName -- 根据客户名称查询
*
* 再springdataJpa的运行阶段
* 会根据方法名称进行解析 findBy from xxx(实体类)
* 属性名称 where custName =
*
* 1.findBy + 属性名称 (根据属性名称进行完成匹配的查询=)
* 2.findBy + 属性名称 + “查询方式(Like | isnull)”
* findByCustNameLike
* 3.多条件查询
* findBy + 属性名 + “查询方式” + “多条件的连接符(and|or)” + 属性名 + “查询方式”
*/
public Customer findByCustName(String custName);
//方法命名方式查询
public List<Customer> findByCustNameLike(String custName);
方法命名方式查询,使用客户名称模糊匹配和客户所属行业精准匹配的查询
public Customer findByCustNameLikeAndCustIndustry(String custName,String custIndustry);
}
4.2 方法命名方式查询
Keyword | Sample | JPQL |
---|---|---|
And | findByLastnameAndFirstname | … where x.lastname = ?1 and x.firstname = ?2 |
Or | findByLastnameOrFirstname | … where x.lastname = ?1 or x.firstname = ?2 |
Is,Equals | findByFirstnameIs,findByFirstnameEquals | … where x.firstname = ?1 |
Between | findByStartDateBetween | … where x.startDate between ?1 and ?2 |
LessThan | findByAgeLessThan | … where x.age < ?1 |
LessThanEqual | findByAgeLessThanEqual | … where x.age ⇐ ?1 |
GreaterThan | findByAgeGreaterThan | … where x.age > ?1 |
GreaterThanEqual | findByAgeGreaterThanEqual | … where x.age >= ?1 |
After | findByStartDateAfter | … where x.startDate > ?1 |
Before | findByStartDateBefore | … where x.startDate < ?1 |
IsNull | findByAgeIsNull | … where x.age is null |
IsNotNull,NotNull | findByAge(Is)NotNull | … where x.age not null |
Like | findByFirstnameLike | … where x.firstname like ?1 |
NotLike | findByFirstnameNotLike | … where x.firstname not like ?1 |
StartingWith | findByFirstnameStartingWith | … where x.firstname like ?1 (parameter bound with appended %) |
EndingWith | findByFirstnameEndingWith | … where x.firstname like ?1 (parameter bound with prepended %) |
Containing | findByFirstnameContaining | … where x.firstname like ?1 (parameter bound wrapped in %) |
OrderBy | findByAgeOrderByLastnameDesc | … where x.age = ?1 order by x.lastname desc |
Not | findByLastnameNot | … where x.lastname <> ?1 |
In | findByAgeIn(Collection ages) | … where x.age in ?1 |
NotIn | findByAgeNotIn(Collection age) | … where x.age not in ?1 |
TRUE | findByActiveTrue() | … where x.active = true |
FALSE | findByActiveFalse() | … where x.active = false |
IgnoreCase | findByFirstnameIgnoreCase | … where UPPER(x.firstame) = UPPER(?1) |
5、测试
5.1 测试自己写的jpql语句
import cn.itcast.dao.CustomerDao;
import cn.itcast.domain.Customer;
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 java.util.Arrays;
import java.util.List;
@RunWith(SpringJUnit4ClassRunner.class) //声明spring提供的单元测试环境
@ContextConfiguration(locations = "classpath:applicationContext.xml")//指定spring容器的配置信息
public class JpqlTest {
@Autowired
private CustomerDao customerDao;
@Test
public void testFindJPQL() {
Customer customer = customerDao.findJpql("张三");
System.out.println(customer);
}
@Test
public void testFindCustNameAndId() {
// Customer customer = customerDao.findCustNameAndId("张三",1l);
Customer customer = customerDao.findCustNameAndId(1l,"张三");
System.out.println(customer);
}
/**
* 测试jpql的更新操作
* * springDataJpa中使用jpql完成 更新/删除操作
* * 需要手动添加事务的支持
* * 默认会执行结束之后,回滚事务
* @Rollback : 设置是否自动回滚
* false | true
*/
@Test
@Transactional //添加事务的支持
@Rollback(value = false)
public void testUpdateCustomer() {
customerDao.updateCustomer(4l,"程序员");
}
//测试sql查询
@Test
public void testFindSql() {
List<Object[]> list = customerDao.findSql("张三%");
for(Object [] obj : list) {
System.out.println(Arrays.toString(obj));
}
}
//测试方法命名规则的查询
@Test
public void testNaming() {
Customer customer = customerDao.findByCustName("张三");
System.out.println(customer);
}
//测试方法命名规则的查询
@Test
public void testFindByCustNameLike() {
List<Customer> list = customerDao.findByCustNameLike("张三%");
for (Customer customer : list) {
System.out.println(customer);
}
}
//测试方法命名规则的查询
@Test
public void testFindByCustNameLikeAndCustIndustry() {
Customer customer = customerDao.findByCustNameLikeAndCustIndustry("张三%", "it");
System.out.println(customer);
}
}
5.2 测试继承的类中的方法
import cn.itcast.dao.CustomerDao;
import cn.itcast.domain.Customer;
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 org.springframework.transaction.annotation.Transactional;
import java.util.List;
@RunWith(SpringJUnit4ClassRunner.class) //声明spring提供的单元测试环境
@ContextConfiguration(locations = "classpath:applicationContext.xml")//指定spring容器的配置信息
public class CustomerDaoTest {
@Autowired
private CustomerDao customerDao;
/**
* 根据id查询
*/
@Test
public void testFindOne() {
Customer customer = customerDao.findOne(4l);
System.out.println(customer);
}
/**
* save : 保存或者更新
* 根据传递的对象是否存在主键id,
* 如果没有id主键属性:保存
* 存在id主键属性,根据id查询数据,更新数据
*/
@Test
public void testSave() {
Customer customer = new Customer();
customer.setCustName("程序员");
customer.setCustLevel("vip");
customer.setCustIndustry("it");
customerDao.save(customer);
}
@Test
public void testUpdate() {
Customer customer = new Customer();
customer.setCustId(4l);
customer.setCustName("程序员很厉害");
customerDao.save(customer);
}
@Test
public void testDelete () {
customerDao.delete(3l);
}
/**
* 查询所有
*/
@Test
public void testFindAll() {
List<Customer> list = customerDao.findAll();
for(Customer customer : list) {
System.out.println(customer);
}
}
/**
* 测试统计查询:查询客户的总数量
* count:统计总条数
*/
@Test
public void testCount() {
long count = customerDao.count();//查询全部的客户数量
System.out.println(count);
}
/**
* 测试:判断id为4的客户是否存在
* 1. 可以查询以下id为4的客户
* 如果值为空,代表不存在,如果不为空,代表存在
* 2. 判断数据库中id为4的客户的数量
* 如果数量为0,代表不存在,如果大于0,代表存在
*/
@Test
public void testExists() {
boolean exists = customerDao.exists(4l);
System.out.println("id为4的客户 是否存在:"+exists);
}
/**
* 根据id从数据库查询
* @Transactional : 保证getOne正常运行
*
* findOne:
* em.find() :立即加载
* getOne:
* em.getReference :延迟加载
* * 返回的是一个客户的动态代理对象
* * 什么时候用,什么时候查询
*/
@Test
@Transactional
public void testGetOne() {
Customer customer = customerDao.getOne(4l);
System.out.println(customer);
}
}
6、Specifications
动态查询
之前dao实现了两个接口:JpaRepository
和JpaSpecificationExecutor
,以上的示例只是展示了JpaRepository
,接下来就是JpaSpecificationExecutor
。
有时我们在查询某个实体的时候,给定的条件是不固定的,这时就需要动态构建相应的查询语句,在Spring Data JPA中可以通过JpaSpecificationExecutor接口查询。相比JPQL,其优势是类型安全,更加的面向对象。
6.1 JpaSpecificationExecutor
中的方法
/*
* Copyright 2008-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.jpa.repository;
import java.util.List;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
/**
* Interface to allow execution of {@link Specification}s based on the JPA criteria API.
*
* @author Oliver Gierke
*/
public interface JpaSpecificationExecutor<T> {
/**
* Returns a single entity matching the given {@link Specification}.
* 根据条件查询一个对象
* @param spec
* @return
*/
T findOne(Specification<T> spec);
/**
* Returns all entities matching the given {@link Specification}.
* 根据条件查询集合
* @param spec
* @return
*/
List<T> findAll(Specification<T> spec);
/**
* Returns a {@link Page} of entities matching the given {@link Specification}.
* 根据条件分页查询
* @param spec
* @param pageable
* @return
*/
Page<T> findAll(Specification<T> spec, Pageable pageable);
/**
* Returns all entities matching the given {@link Specification} and {@link Sort}.
* 排序查询查询
* @param spec
* @param sort
* @return
*/
List<T> findAll(Specification<T> spec, Sort sort);
/**
* Returns the number of instances that the given {@link Specification} will return.
* 统计查询
* @param spec the {@link Specification} to count instances for
* @return the number of instances
*/
long count(Specification<T> spec);
}
6.2 Specification
对于JpaSpecificationExecutor
,这个接口基本是围绕着Specification
接口来定义的。我们可以简单的理解为,Specification
构造的就是查询条件
Specification
接口中只定义了如下一个方法:
//构造查询条件
/**
* root :Root接口,代表查询的根对象,可以通过root获取实体中的属性
* query :代表一个顶层查询对象,用来自定义查询
* cb :用来构建查询,此对象里有很多条件方法
**/
public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb);
6.3 findOne
6.3.1 根据条件,查询单个对象
/**
* 根据条件,查询单个对象
*
*/
@Test
public void testSpec() {
//匿名内部类
/**
* 自定义查询条件
* 1.实现Specification接口(提供泛型:查询的对象类型)
* 2.实现toPredicate方法(构造查询条件)
* 3.需要借助方法参数中的两个参数(
* root:获取需要查询的对象属性
* CriteriaBuilder:构造查询条件的,内部封装了很多的查询条件(模糊匹配,精准匹配)
* )
* 案例:根据客户名称查询,查询客户名为科技公司的客户
* 查询条件
* 1.查询方式
* cb对象
* 2.比较的属性名称
* root对象
*
*/
Specification<Customer> spec = new Specification<Customer>() {
@Override
public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
//1.获取比较的属性
Path<Object> custName = root.get("custId");
//2.构造查询条件 : select * from cst_customer where cust_name = '科技公司'
/**
* 第一个参数:需要比较的属性(path对象)
* 第二个参数:当前需要比较的取值
*/
Predicate predicate = cb.equal(custName, "程序员");//进行精准的匹配 (比较的属性,比较的属性的取值)
return predicate;
}
};
Customer customer = customerDao.findOne(spec);
System.out.println(customer);
}
6.3.2 多条件查询
/**
* 多条件查询
* 案例:根据客户名(科技公司)和客户所属行业查询(it教育)
*
*/
@Test
public void testSpec1() {
/**
* root:获取属性
* 客户名
* 所属行业
* cb:构造查询
* 1.构造客户名的精准匹配查询
* 2.构造所属行业的精准匹配查询
* 3.将以上两个查询联系起来
*/
Specification<Customer> spec = new Specification<Customer>() {
@Override
public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
Path<Object> custName = root.get("custName");//客户名
Path<Object> custIndustry = root.get("custIndustry");//所属行业
//构造查询
//1.构造客户名的精准匹配查询
Predicate p1 = cb.equal(custName, "程序员");//第一个参数,path(属性),第二个参数,属性的取值
//2..构造所属行业的精准匹配查询
Predicate p2 = cb.equal(custIndustry, "it");
//3.将多个查询条件组合到一起:组合(满足条件一并且满足条件二:与关系,满足条件一或满足条件二即可:或关系)
Predicate and = cb.and(p1, p2);//以与的形式拼接多个查询条件
// cb.or();//以或的形式拼接多个查询条件
return and;
}
};
Customer customer = customerDao.findOne(spec);
System.out.println(customer);
}
示例
// select * from user where id='c7825c1acede475fa04bc7189929e6ce' and dept='研发部' and (name='zhangsan' OR name='lisi')
List<User> users = userDao.findAll(new Specification<User>() {
@Override
public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
return criteriaBuilder.and(
criteriaBuilder.equal(root.get("id"),id),
criteriaBuilder.equal(root.get("dept"),"研发部"),
criteriaBuilder.or(
criteriaBuilder.equal(root.get("name"),"zhangsan"),
criteriaBuilder.equal(root.get("name"),"lisi")
)
);
}
});
6.3.3 in查询
List<Customer> customers = CustomerDao.findAll(new Specification<Customer>() {
@Override
public Predicate toPredicate(Root<Spbussprocess> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
List<String> strings = spbusses.stream().map(Spbuss::getId).collect(Collectors.toList());
return root.get("id").in(strings);
}
});
6.3.4 排序
List<User> users= userDao.findAll(new Specification<User>() {
@Override
public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
//按照cretime字段和updtime字段排序
query.orderBy(criteriaBuilder.desc(root.get("age")), criteriaBuilder.desc(root.get("dept")));
return criteriaBuilder.and(
criteriaBuilder.equal(root.get("name"), "zhangsan"),
criteriaBuilder.equal(root.get("age"), "20")
);
}
});
6.4 findAll
6.4.1 查询所有
/**
* 案例:完成根据客户名称的模糊匹配,返回客户列表
* 客户名称以 ’科技公司‘ 开头
*
* equal :直接的到path对象(属性),然后进行比较即可
* gt,lt,ge,le,like : 得到path对象,根据path指定比较的参数类型,再去进行比较
* 指定参数类型:path.as(类型的字节码对象)
*/
@Test
public void testSpec3() {
//构造查询条件
Specification<Customer> spec = new Specification<Customer>() {
@Override
public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
//查询属性:客户名
Path<Object> custName = root.get("custName");
//查询方式:模糊匹配
Predicate like = cb.like(custName.as(String.class), "科技公司%");
return like;
}
};
// List<Customer> list = customerDao.findAll(spec);
// for (Customer customer : list) {
// System.out.println(customer);
// }
//添加排序
//创建排序对象,需要调用构造方法实例化sort对象
//第一个参数:排序的顺序(倒序,正序)
// Sort.Direction.DESC:倒序
// Sort.Direction.ASC : 升序
//第二个参数:排序的属性名称
Sort sort = new Sort(Sort.Direction.DESC,"custId");
List<Customer> list = customerDao.findAll(spec, sort);
for (Customer customer : list) {
System.out.println(customer);
}
}
6.4.2 分页查询所有
/**
* 分页查询
* Specification: 查询条件
* Pageable:分页参数
* 分页参数:查询的页码,每页查询的条数
* findAll(Specification,Pageable):带有条件的分页
* findAll(Pageable):没有条件的分页
* 返回:Page(springDataJpa为我们封装好的pageBean对象,数据列表,共条数)
*/
@Test
public void testSpec4() {
Specification spec = null;
//PageRequest对象是Pageable接口的实现类
/**
* 创建PageRequest的过程中,需要调用他的构造方法传入两个参数
* 第一个参数:当前查询的页数(从0开始)
* 第二个参数:每页查询的数量
*/
Pageable pageable = new PageRequest(0,2);
//分页查询
Page<Customer> page = customerDao.findAll(null, pageable);
System.out.println(page.getContent()); //得到数据集合列表
System.out.println(page.getTotalElements());//得到总条数
System.out.println(page.getTotalPages());//得到总页数
}
6.5 方法对应关系
方法名称 | 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 |
7、ExampleMatcher查询
Xm xm=new Xm();
xm.setId("025e47b62d144e869ee2e9e3112321395cd702");
xm.setXmjd("111");
//多条件and匹配
ExampleMatcher exampleMatcher=ExampleMatcher.matchingAll(); //matchingAll全匹配,有几个参数,都要满足and
//Example.of(查询条件,匹配规则)
Example<Xm> example=Example.of(xm,exampleMatcher);
List<Xm> xms= xmDao.findAll(example);
//一样效果
/* List<Xm> xms = xmDao.findAll(new Example<Xm>() {
@Override
public Xm getProbe() { //查询参数
Xm xm=new Xm();
xm.setId("025e47b62d144e869ee2e9e3195cd702");
xm.setXmjd("111");
return xm;
}
@Override
public ExampleMatcher getMatcher() { //匹配规则
return ExampleMatcher.matchingAll(); //matchingAll全匹配,有几个参数,都要满足and id=? and xmjd=?
}
});*/
匹配规则
方法名称 | Sql对应关系 |
---|---|
exact | = |
startsWith | 开头模糊查询匹配 as% |
startsWith | 结束模糊查询匹配 %as |
contains | 模糊查询 %as% |
//条件匹配
//withMatcher("参数",matcher -> matcher.匹配规则())
ExampleMatcher.matching().withMatcher("id",matcher -> matcher.exact()).withMatcher("xmjd",matcher -> matcher.exact());
https://jiuaidu.com/jianzhan/667681/
8、JPA的事务
JPA控制事务一般使用注解:@Transactional(rollbackFor = Exception.class)
但是方法太多了,就要写很多注解,很乱,而且不好管理,使用配置类:
需要用到execution
表达式,资料:https://blog.csdn.net/thinkingcao/article/details/84872610
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.interceptor.TransactionInterceptor;
import java.util.Properties;
@Configuration
@EnableTransactionManagement
public class AspectjTransactionConfig {
// * com.wzw..*(..) 所有类型的方法,com.wzw.包以及com.wzw.的子包下的所有方法
// * com.wzw.service.*(..) com.wzw.service下的所有方法
public static final String transactionExecution = "execution (* com.wzw..*(..))";
@Autowired
private PlatformTransactionManager transactionManager;
@Bean
public DefaultPointcutAdvisor defaultPointcutAdvisor() {
//指定一般要拦截哪些类
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression(transactionExecution);
//配置advisor
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();
advisor.setPointcut(pointcut);
//指定不同的方法用不通的策略
Properties attributes = new Properties();
attributes.setProperty("get*", "PROPAGATION_REQUIRED,-Exception");
attributes.setProperty("add*", "PROPAGATION_REQUIRED,-Exception");
attributes.setProperty("save*", "PROPAGATION_REQUIRED,-Exception");
attributes.setProperty("update*", "PROPAGATION_REQUIRED,-Exception");
attributes.setProperty("delete*", "PROPAGATION_REQUIRED,-Exception");
//创建Interceptor
TransactionInterceptor txAdvice = new TransactionInterceptor(transactionManager, attributes);
advisor.setAdvice(txAdvice);
return advisor;
}
}
可以使用 @Transactional 来覆盖全局的配置
transactionAttributes属性:https://www.cnblogs.com/peterkang202/p/6045832.html
9、常用注解
1、Entity 表示这个类是一个实体类,对应数据库中的一个表
2、@Table 指定这个类对应数据库中的表名。如果这个类名的命名方式符合数据库的命名方式,可以省略这个注解。如FlowType类名对应表名flow_type。
3、@Id 指定这个字段为表的主键
4、@GeneratedValue(strategy=GenerationType.IDENTITY) 指定主键的生成方式,一般主键为自增的话,就采用GenerationType.IDENTITY的生成方式
5、@Column(updatable = false) @Columun 注解针对一个字段,对应表中的一列。有很多参数,name表示对应数据表中的字段名。insertable 表示插入式是否更新。updateable,表示update的时候是否更新;columnDefinition表示字段类型,当使用jpa自动生成表的时候比较有用。
6、@PrePersist 表示持久化之前执行
7、@PreUpdate 表示执行Update操作之前执行。
8、SQLDelete表示当执行jpa中的delete操作时,执行的语句
9、@Where 当执行查询语句时,会附带这个条件。这个和上面的一起使用,可以实现软删除
10、其它
10.1 自动生成主键
@Id
@GeneratedValue(generator="system_uuid")
@GenericGenerator(name="system_uuid",strategy="uuid")
/**
* 主键
*/
@Column(name="id")
private String id;
10.2 save方法自动填充修改和创建时间
- 启动类添加
@EnableJpaAuditing
注解 - 对应的实体类添加
@EntityListeners(AuditingEntityListener.class)
注解,对应字段添加@CreatedDate
(创建时间)和@LastModifiedDate
(修改时间)
10.2.1 修改的时候,@CreatedDate值为空了
使用属性updatable
/**
* 创建时间
*/
@Column(name="cretime",updatable = false)
@CreatedDate
private Date cretime;
@Column属性:
参数 | 类型 | 描述 |
---|---|---|
name | String | 列的名称,默认为属性的名称(Hibernate 映射列时,若遇到驼峰拼写,会自动添加 _ 连接并将大写字母改成小写)。 |
unique | boolean | 列的值是否是唯一的。这是 @UniqueConstraint 注解的一个快捷方式, 实质上是在声明唯一约束。默认值为 false。 |
nullable | boolean | 列的值是否允许为 null。默认为 true。 |
insertable | boolean | 列是否包含在 INSERT 语句中,默认为 true。 |
updatable | boolean | 列是否包含在 UPDATE 语句中,默认为 true。 |
columnDefinition | String | 生成列的 DDL 时使用的 SQL 片段。默认使用推断的类型来生成 SQL 片段以创建此列。 |
table | String | 当前列所属的表的名称。 |
length | int | 列的长度,仅对字符串类型的列生效。默认为255。 |
precision | int | 列的精度,仅对十进制数值有效,表示有效数值的总位数。默认为0。 |
scale | int | 列的精度,仅对十进制数值有效,表示小数位的总位数。默认为0。 |
10.3 @Query传参的两种方式
- 位置参数
@Query("select u from User u where u.emailAddress = ?1")
User findByEmailAddress(String emailAddress);
- 命名参数
@Query("select u from User u where u.emailAddress = :emailAddress")
User findByEmailAddress(@Param("emailAddress") String emailAddress);
10.4 直接保存为json对象到数据库
实体类中对应属性使用两个注解
@Type(type = "json")
//数据库对应字段名,数据库字段格式为文本格式,text,json,varchar
@Column(name = "users",columnDefinition = "json")
private List<User> users;
10.5 修改dao查询到的对象属性,没有做保存操作,数据库的值也被修改了
网上解答:
JPA有如上所示的四个生命周期:
New:瞬时对象,尚未有id,还未和Persistence Context建立关联的对象。
Managed:持久化受管对象,有id值,已经和Persistence Context建立了关联的对象。
Datached:游离态离线对象,有id值,但没有和Persistence Context建立关联的对象。
Removed:删除的对象,有id值,尚且和Persistence Context有关联,但是已经准备好从数据库中删除
当从数据库获取的数据后,因为有事务管理,所以数据已与session关联,并且数据库有数据,已经持久化了,并且在数据库的缓存当中了,当我们对查询出来的数据进行修改时,缓存缓存Session中的数据发生改变,那么接着数据库也会跟着进行相应的改变。所以就自动执行了update的更新操作。
解决办法:
一、new一个新的对象,拷贝属性给新对象,操作新对象的值,
BeanUtils.copyProperties(原对象,新对象);
二、如果使用的是EntityManager,有个entityManager.clear();方法,clear就好
11、分页
方法命名方式
dao层
@Repository
public interface UserDao extends GenericJpaRepository<User, String>, JpaSpecificationExecutor<User> {
Page<User> findByName(String name, Pageable pageable);
}
service层
@RequestMapping("/test")
public Page<User> getUsers() {
Pageable pageable = PageRequest.of(1, 6);
Page<User> results = userDao.findByName("zhangsan", pageable);
return results;
}
Specifications动态查询
@RequestMapping("/test")
public Page<User> getSpecificationsByName() {
Specification<User> specification = new Specification<>() {
@Override
public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
return criteriaBuilder.equal(root.get("name"), "zhangsan");
}
};
//参数1,第几页,参数二,每页条数
Pageable pageable = PageRequest.of(1, 6);
Page<User> results = UserDao.findAll(specification,pageable);
//简写
Page<User> results1 = UserDao.findAll((r,q,c)->c.equal(r.get("name"), "zhangsan"),pageable);
return results;
}
报错
Inferred type ‘S’ for type parameter ‘S’ is not within its bound; should ext
确定接口中的类是否正确
public interface PtBusinessLogsDao extends JpaRepository<PtBusinessLogs
,String>, JpaSpecificationExecutor<PtBusinessLogs
> {