Spring Data 项目的目的是为了简化构建基于 Spring 框架应用的数据访问计数,包括非关系数据库、Map-Reduce 框架、云数据服务等等;另外也包含对关系数据库的访问支持。
这里引用一下博客园 神一样存在 的一篇博客的内容
(原文:http://www.cnblogs.com/dreamroute/p/5173896.htm )
JPA诞生的缘由是为了整合第三方ORM框架,建立一种标准的方式,百度百科说是JDK为了实现 ORM的天下归一,目前也是在按照这个方向发展,但是还没能完全实现。在ORM框架中,Hibernate是一支很大的部队,使用很广泛,也很方便,能力 也很强,同时Hibernate也是和JPA整合的比较良好,我们可以认为JPA是标准,事实上也是,JPA几乎都是接口,实现都是Hibernate在 做,宏观上面看,在JPA的统一之下Hibernate很良好的运行。
上面阐述了JPA和Hibernate的关系,那么Spring-data-jpa又是个什么东西呢?这地方需要稍微解释一下,我们做Java开 发的都知道Spring的强大,到目前为止,企业级应用Spring几乎是无所不能,无所不在,已经是事实上的标准了,企业级应用不使用Spring的几 乎没有,这样说没错吧。而Spring整合第三方框架的能力又很强,他要做的不仅仅是个最早的IOC容器这么简单一回事,现在Spring涉及的方面太 广,主要是体现在和第三方工具的整合上。而在与第三方整合这方面,Spring做了持久化这一块的工作,我个人的感觉是Spring希望把持久化这块内容 也拿下。于是就有了Spring-data-**这一系列包。包括,Spring-data-jpa,Spring-data- template,Spring-data-mongodb,Spring-data-redis,还有个民间产品,mybatis-spring,和前 面类似,这是和mybatis整合的第三方包,这些都是干的持久化工具干的事儿。
这里介绍Spring-data-jpa,表示与jpa的整合。
我们都知道,在使用持久化工具的时候,一般都有一个对象来操作数据库,在原生的Hibernate中叫做Session,在JPA中叫做 EntityManager,在MyBatis中叫做SqlSession,通过这个对象来操作数据库。我们一般按照三层结构来看的话,Service层 做业务逻辑处理,Dao层和数据库打交道,在Dao中,就存在着上面的对象。那么ORM框架本身提供的功能有什么呢?答案是基本的CRUD,所有的基础 CRUD框架都提供,我们使用起来感觉很方便,很给力,业务逻辑层面的处理ORM是没有提供的,如果使用原生的框架,业务逻辑代码我们一般会自定义,会自 己去写SQL语句,然后执行。在这个时候,Spring-data-jpa的威力就体现出来了,ORM提供的能力他都提供,ORM框架没有提供的业务逻辑 功能Spring-data-jpa也提供,全方位的解决用户的需求。使用Spring-data-jpa进行开发的过程中,常用的功能,我们几乎不需要 写一条sql语句,至少在我看来,企业级应用基本上可以不用写任何一条sql,当然spring-data-jpa也提供自己写sql的方式,这个就看个 人怎么选择,都可以。我觉得都行。
@Entity
@Table(name = "T_COURIER")
public class Courier {
@Id
@GeneratedValue
@Column(name = "C_ID")
private Integer id;
@Column(name = "C_COURIER_NUM", unique = true)
private String courierNum;
@Column(name = "C_NAME")
private String name;
@ManyToOne
@JoinColumn(name = "C_STANDARD_ID")
private Standard standard;
@Entity
@Table(name = "T_STANDARD")
public class Standard {
@Id
@GeneratedValue
@Column(name = "C_ID")
private Integer id;
@Column(name = "C_NAME")
private String name;
@Column(name = "C_OPERATOR")
private String operator;
@Column(name = "C_OPERATING_COMPANY")
private String operatingCompany;
在spring的applicationContext.xml引入spring data jpa名称空间
<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"
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 ">
配置entityManagerFactory ,JPA编程核心接口EntityManager (类似Session,进行增删改查 )
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" value="cn.roger.domain" />
<property name="persistenceProvider">
<bean class="org.hibernate.jpa.HibernatePersistenceProvider" />
property>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="generateDdl" value="true" />
<property name="database" value="ORACLE" />
<property name="databasePlatform" value="org.hibernate.dialect.Oracle10gDialect" />
<property name="showSql" value="true" />
bean>
property>
<property name="jpaDialect">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
property>
<property name="jpaPropertyMap">
<map>
<entry key="hibernate.query.substitutions" value="true 1, false 0" />
<entry key="hibernate.default_batch_fetch_size" value="16" />
<entry key="hibernate.max_fetch_depth" value="2" />
<entry key="hibernate.generate_statistics" value="true" />
<entry key="hibernate.bytecode.use_reflection_optimizer"
value="true" />
<entry key="hibernate.cache.use_second_level_cache" value="false" />
<entry key="hibernate.cache.use_query_cache" value="false" />
map>
property>
bean>
基于jpa进行事务管理
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
扫描DAO接口,自动生成实现类
<jpa:repositories base-package="cn.roger.dao" />
完整的xml配置:
<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"
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 ">
<context:component-scan base-package="cn.roger"/>
<context:property-placeholder location="classpath:config.properties" />
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}" />
<property name="jdbcUrl" value="${jdbc.url}" />
<property name="user" value="${jdbc.user}" />
<property name="password" value="${jdbc.password}" />
bean>
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" value="cn.roger.domain" />
<property name="persistenceProvider">
<bean class="org.hibernate.jpa.HibernatePersistenceProvider" />
property>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="generateDdl" value="true" />
<property name="database" value="ORACLE" />
<property name="databasePlatform" value="org.hibernate.dialect.Oracle10gDialect" />
<property name="showSql" value="true" />
bean>
property>
<property name="jpaDialect">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
property>
<property name="jpaPropertyMap">
<map>
<entry key="hibernate.query.substitutions" value="true 1, false 0" />
<entry key="hibernate.default_batch_fetch_size" value="16" />
<entry key="hibernate.max_fetch_depth" value="2" />
<entry key="hibernate.generate_statistics" value="true" />
<entry key="hibernate.bytecode.use_reflection_optimizer"
value="true" />
<entry key="hibernate.cache.use_second_level_cache" value="false" />
<entry key="hibernate.cache.use_query_cache" value="false" />
map>
property>
bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
<jpa:repositories base-package="cn.roger.dao" />
beans>
DAO的接口:
Repository (空接口)
CrudRepository (增删改查)
PagingAndSortingRepository (分页和排序)
JpaRepository (扩展增删改查、批量操作)
JpaSpecificationExecutor:用来做负责查询的接口
Specification:是Spring Data JPA提供的一个查询规范,要做复杂的查询,类似hibernate QBC查询
JpaRepository继承PagingAndSortingRepository,CrudRepository:
CrudRepository 提供 save、 delete、 deteleAll、 findAll、 findOne、 count等方法
PagingAndSortingRepository提供排序及分页findAll(Sort) (基于排序的查询)、findAll(Pageable)( 基于分页的查询)
所以继承JpaRepository 就拥有父类的方法,而父类具体实现(simpleJpaRepository)由spring 容器来调用(动态代理)ORM框架来完成ORID的操作。
//JpaRepository第一个参数是实体类,第二个参数是实体的主键类型
public interface CourierRepository extends JpaRepository<Courier, Integer>,
JpaSpecificationExecutor<Courier> {
}
public interface StandardRepository extends JpaRepository<Standard, Integer> {
}
Keyword | Sample | JPQL snippet |
---|---|---|
And | findByLastnameAndFirstname | … where x.lastname = ?1 and x.firstname = ?2 |
Or | findByLastnameOrFirstname | … where x.lastname = ?1 or x.firstname = ?2 |
Is,Equals | findByFirstname,findByFirstnameIs | |
Between | findByStartDateBetween | … where x.startDatebetween 1? and ?2 |
LessThan | findByAgeLessThan | … where x.age < ?1 LessThanEqual findByAgeLessThanEqual |
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) |
Spring data Query 使用实现条件查询JPA 提供了以下几种查询方式:
根据方法命名规则自动生成:
基于一列等值查询 findBy属性名 例如:findByName(String name)
基于一列模糊查询findBy属性名Like 例如:findByNameLike(String name)
基于两列等值查询findBy属性名And属性名例如:findByUsernameAndPassword(String username,String password)
public interface StandardRepository extends JpaRepository<Standard, Integer> {
//基于一列等值查询 findBy属性名
public List findByName(String name);
//基于一列模糊查询findBy属性名Like
public List findByNameLike(String name);
//基于两列等值查询findBy属性名And属性名
public List findByUsernameAndPassword(String username,String password);
}
不按命名规则写的查询方法,可以配置@Query绑定JPQL语句或者SQL语句
public interface StandardRepository extends JpaRepository<Standard, Integer> {
@Query(value="from Standard where name = ?" ,nativeQuery=false)
// nativeQuery 为 false 配置JPQL 、 为true 配置SQL
public List queryName(String name);
@Query(value="update Standard set minLength=?2 where id =?1")
@Modifying
//带有条件修改和删除操作需要Modifying注解
public void updateMinLength(Integer id , Integer minLength);
}
nativeQuery 为 false 配置JPQL 、 为true 配置SQL .
带有条件修改和删除操作需要Modifying注解。
注意:方法参数位置顺序,如果在JPQL语句中参数没有标明位置,那么需要方法参数对调。
不按命名规则写的查询方法,配置@Query,不用写JPQL语句
public interface StandardRepository extends JpaRepository<Standard, Integer> {
@Query
public List queryName(String name);
}
需要在实体类上写@NamedQueries,这种会带来代码之间的耦合,一般不会使用。
@Entity
@Table(name = "T_STANDARD")
@NamedQueries({
@NamedQuery(name="Standard.queryName2",query="from Standard where name=?")})
public class Standard {
JPA的Criteria查询
接口只需要继承JpaSpecificationExecutor
public interface CourierRepository extends JpaRepository<Courier, Integer>,
JpaSpecificationExecutor<Courier> {
}
条件查询的方法:
List
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:applicationContext.xml")
public class StandardRepositoryTest {
@Autowired
private StandardRepository standardRepository;
@Test
public void testQuery(){
System.out.println(standardRepository.queryName("Job"));
}
@Test
@Transactional
@Rollback(false)
public void testUpdate(){
standardRepository.updateName("Job", "Roger");
}
}
public class CourierAction extends ActionSupport implements
ModelDriven {
// 模型驱动
private Courier courier = new Courier();
@Override
public Courier getModel() {
return courier;
}
// 注入Service
@Autowired
private CourierService courierService;
// 增加的方法
public String save() {
courierService.save(courier);
return SUCCESS;
}
// 属性驱动接收客户端分页参数
//分页查询需要page与rows两个属性
private int page;
private int rows;
public void setPage(int page) {
this.page = page;
}
public void setRows(int rows) {
this.rows = rows;
}
// 分页查询方法
public String pageQuery() {
// 封装Pageable对象
//page一般是从1开始的,分页查询是从0开始的,所以需要减1
Pageable pageable = new PageRequest(page - 1, rows);
// 封装条件查询对象 Specification
Specification specification = new Specification() {
@Override
// Root 用于获取属性字段,CriteriaQuery可以用于简单条件查询,CriteriaBuilder 用于构造复杂条件查询
public Predicate toPredicate(Root root,
CriteriaQuery> query, CriteriaBuilder cb) {
List list = new ArrayList();
// 简单单表查询
if (StringUtils.isNotBlank(courier.getCourierNum())) {
Predicate p1 = cb.equal(
root.get("courierNum").as(String.class),
courier.getCourierNum());
list.add(p1);
}
if (StringUtils.isNotBlank(courier.getCompany())) {
Predicate p2 = cb.like(
root.get("company").as(String.class),
"%" + courier.getCompany() + "%");
list.add(p2);
}
if (StringUtils.isNotBlank(courier.getType())) {
Predicate p3 = cb.equal(root.get("type").as(String.class),
courier.getType());
list.add(p3);
}
// 多表查询
Join standardJoin = root.join("standard",
JoinType.INNER);
if (courier.getStandard() != null
&& StringUtils.isNotBlank(courier.getStandard()
.getName())) {
Predicate p4 = cb.like(
standardJoin.get("name").as(String.class), "%"
+ courier.getStandard().getName() + "%");
list.add(p4);
}
return cb.and(list.toArray(new Predicate[0]));
}
};
// 调用业务层 ,返回 Page
Page pageData = courierService.findPageData(specification,
pageable);
// 将返回page对象
Map result = new HashMap();
result.put("total", pageData.getTotalElements());
result.put("rows", pageData.getContent());
// 将结果对象 压入值栈顶部
ActionContext.getContext().getValueStack().push(result);
return SUCCESS;
}
}
Specification对象:
toPredicate方法中Root 用于获取属性字段,CriteriaQuery可以用于简单条件查询,CriteriaBuilder 用于构造复杂条件查询