1. Spring Data JPA是什么

Spring Data JPA是Spring Data大家族中的一员,它对对持久层做了简化,用户只需要声明方法的接口,不需要实现该接口,Spring Data JPA内部会根据不同的策略、通过不同的方法创建Query操作数据库。

使用Spring Data JPA可以在几乎不用写实现的情况下实现对数据的访问和操作,除了CRUD之外还有分页、排序等常用功能。

比如,当你看到UserDao.findUserById() 这样一个方法声明,大致应该能判断出这是根据给定条件的 ID 查询出满足条件的 User 对象。Spring Data JPA 做的便是规范方法的名字,根据符合规范的名字来确定方法需要实现什么样的逻辑。

2. 如何使用

使用Spring Data JPA的套路

1. 声明持久层的接口,该接口继承Repository,Repository是一个标记型的接口,它不包含任何方法,Spring Data JPA也提供了一些Repository的一些子接口,其中定义了一些常用的增删改查,以及分页相关的方法,我们一般并不会直接继承Repository,而是继承他的一个子类叫做JpaRepository。

2. 在接口中声明需要的业务方法,Spring Data将根据特定的策略来为其生成实现代码。

3. 在 Spring 配置文件中增加一行声明,让 Spring 为声明的接口创建代理对象。配置了 <jpa:repositories> 后,Spring 初始化容器时将会扫描 base-package 指定的包目录及其子目录,为继承 Repository 或其子接口的接口创建代理对象,并将代理对象注册为Spring Bean,业务层便可以通过 Spring 自动封装的特性来直接使用该对象。

举个栗子

项目结构如下:

Spring Data JPA的配置文件叫做persistence.xml,网上说的必须放在META-INF下面,偶不信邪,只要开发者脑子没坑的话设计基本就是给一个默认的,然后提供setter来让用户定制,直接去源代码中看,有一个叫做DefaultPersistenceUnitManager的类,其中有一个叫做DEFAULT_PERSISTENCE_XML_LOCATION的成员变量提供了默认的配置文件位置,还有一个persistenceXmlLocations可以用来设置配置文件的位置:

public class DefaultPersistenceUnitManager implements PersistenceUnitManager, ResourceLoaderAware, LoadTimeWeaverAware, InitializingBean {

    /** 这个保存了默认的配置文件位置 */
public static final String DEFAULT_PERSISTENCE_XML_LOCATION = "classpath*:META-INF/persistence.xml"; .......... /** 可以看到默认就已经有一个了,我们可以调用这个属性的setter来设置*/
private String[] persistenceXmlLocations = new String[]{"classpath*:META-INF/persistence.xml"}; .......... /** setter方法在这里 */
public void setPersistenceXmlLocation(String persistenceXmlLocation) {
this.persistenceXmlLocations = new String[]{persistenceXmlLocation};
} public void setPersistenceXmlLocations(String... persistenceXmlLocations) {
this.persistenceXmlLocations = persistenceXmlLocations;
} ......... }
在Spring的配置文件中指定配置文件的位置即可,Spring的配置文件applicationContext.xml:
<?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:context="http://www.springframework.org/schema/context"
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-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/data/jpa
http://www.springframework.org/schema/data/jpa/spring-jpa.xsd"> <context:annotation-config></context:annotation-config>
<context:component-scan base-package="org.cc11001100.spring.data.jpa"></context:component-scan> <!-- =============================== Spring Data JPA 相关配置 =============================== --> <!-- 配置实体管理器 -->
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="generateDdl" value="true" />
<property name="database" value="MYSQL" />
</bean>
</property>
<property name="persistenceUnitName" value="SimpleJPA" />
<property name="persistenceXmlLocation" value="persistence.xml" />
</bean> <!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean> <!-- 配置Spring Data JPA扫描目录 -->
<jpa:repositories base-package="org.cc11001100.spring.data.jpa.dao"
entity-manager-factory-ref="entityManagerFactory"
transaction-manager-ref="transactionManager"/> </beans>
JPA的配置文件persistence.xml:
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0"> <persistence-unit name="SimpleJPA" transaction-type="RESOURCE_LOCAL"> <provider>org.hibernate.ejb.HibernatePersistence</provider>
<class>org.cc11001100.spring.data.jpa.domain.User</class> <properties>
<property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/>
<property name="hibernate.connection.url" value="jdbc:mysql:///test_jpa"/>
<property name="hibernate.connection.username" value="root"/>
<property name="hibernate.connection.password" value="toor"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect"/>
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.format_sql" value="true"/>
<property name="hibernate.use_sql_comments" value="false"/>
<property name="hibernate.hbm2ddl.auto" value="update"/>
</properties> </persistence-unit> </persistence>

相关的Java文件域模型User.java:

package org.cc11001100.spring.data.jpa.domain;

import javax.persistence.*;

@Entity
@Table(name = "t_user")
public class User { @Id
@GeneratedValue(strategy= GenerationType.AUTO)
private Long id; private String username; private String passwd; public Long getId() {
return id;
} public void setId(Long id) {
this.id = id;
} public String getUsername() {
return username;
} public void setUsername(String username) {
this.username = username;
} public String getPasswd() {
return passwd;
} public void setPasswd(String passwd) {
this.passwd = passwd;
}
}

Dao层UserDao.java:

package org.cc11001100.spring.data.jpa.dao;

import org.cc11001100.spring.data.jpa.domain.User;
import org.springframework.data.jpa.repository.JpaRepository; public interface UserDao extends JpaRepository<User, Long> { }
Service层接口声明UserService.java:
package org.cc11001100.spring.data.jpa.service;

import org.cc11001100.spring.data.jpa.domain.User;
import org.springframework.stereotype.Service; import java.util.List; public interface UserService { User findById(long id); User save(User user); List<User> findAll(); }

Service实现UserServiceImpl.java:

package org.cc11001100.spring.data.jpa.service;

import org.cc11001100.spring.data.jpa.dao.UserDao;
import org.cc11001100.spring.data.jpa.domain.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import java.util.List; @Service("userService")
public class UserServiceImpl implements UserService { @Autowired
private UserDao userDao; public User findById(long id) {
return userDao.findOne(id);
} public User save(User user) {
return userDao.save(user);
} public List<User> findAll() {
return userDao.findAll();
} }

测试类:

package org.cc11001100.spring.data.jpa;

import org.cc11001100.spring.data.jpa.domain.User;
import org.cc11001100.spring.data.jpa.service.UserService;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext; public class Main_001 { @Test
public void test_001(){ ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = ctx.getBean("userService", UserService.class); User user=new User();
user.setUsername("CC11001100");
user.setPasswd("qwerty");
userService.save(user); } }
MySQL表结构:
CREATE TABLE t_user(
id INT PRIMARY KEY AUTO_INCREMENT ,
username VARCHAR(50) NOT NULL DEFAULT "二狗子" ,
passwd VARCHAR(100) NOT NULL
) CHARSET UTF8;

执行Test:

查询MySQL:

 

@Entity标识了这是一个JPA实体

如果没有使用@Table注解明确指定要持久化到哪一个表中的话,映射到的表就跟类名相同,比如上面的这个就标识这个类映射到的表是User。

@Table 明确指定这个类要映射到哪一个表。

@Id用来标识主键

Spring Data JPA中的仓库继承关系

Repository: 是一个标识接口,方便Spring扫描

CrudRepository: 继承Repository,实现了一组CRUD相关的方法

PagingAndSortingRepository: 继承CrudRepository,实现了分页、排序相关的方法

JpaRepository: 继承PagingAndSortingRepository,实现了一组JPA规范相关的方法,一般都是让Dao层接口继承这个接口。

查询规则

声明接口就直接可用是建立在接口方法命名有一定规则的基础上的,Spring Data JPA通过解析方法的名字来推测它到底想干什么从而为其自动生成代码,解析方法名的时候会首先将方法多余的前缀去掉,比如find、findBy、read、readBy、get、getBy等,然后再对剩下的部分进行解析。

如果方法的最后一个参数是Sort或者Pageable,也会做相关的排序、分页处理

方法名表达式查询关键字

And

相当于SQL中的AND关键字,比如:

User findUserByUsernameAndPasswd(String username, String passwd);

Or

相当于SQL中的OR关键字,比如:

List<User> findByUsernameOrNickname(String username, String nickname);

Not

相当于SQL中的!=,比如:

List<User> findByUsernameNot(String username);

Like

相当于SQL中的LIKE,比如:

List<User> findByUsernameLike(String usernameLike);

需要注意的是这里的?是整个传的,并不会帮我们自动添加%号,所以传入进去的参数应该自己已经拼接好%号了。

NotLike

相当于SQL中的NOT LIKE,比如:

List<User> findByUsernameNotLike(String usernameLike);

In

相当于SQL语句中的IN,比如:

List<User> findByUsernameIn(List<String> usernameList);

NotIn

相当于SQL语句中的NOT IN,比如:

List<User> findByUsernameNotIn(List<String> usernameList);

LessThan

相当于SQL中的小于号,比如:

List<User> findByAgeLessThan(int age);

GreaterThan

相当于SQL中的大于号,比如:

List<User> findByAgeGreaterThan(int age);

Between

相当于SQL中的BETWEEN关键字,比如:

List<User> findByAgeBetween(int min, int max);

IsNull

相当于SQL中的IS NULL,比如:

List<User> findByUsernameIsNull();

IsNotNull

相当于SQL中的IS NOT NULL,比如:

List<User> findByUsernameIsNotNull();

NotNull

相当于IsNotNull。

OrderBy

相当于SQL语句中的ORDER BY。

使用IDEA编辑的时候会有自动提示的,复杂的查询都是一个一个的小表达式使用AND或者OR连起来的,基本元素无非就是这些。

创建查询(Query)

上面的查询规则太复杂了,搞得人晕乎乎的,更适合阅读记忆的方式还是使用@Query注解,可以在Dao层接口方法上添加@Query注解,在其中可以书写SQL语句,调用方法的时候会执行此SQL语句。

@Query有一个参数nativeQuery为true的时候表示前面的SQL语句是原生的SQL语句。

在SQL语句中可以引用接口方法传入的参数,可以使用索引位置来引用,比如使用?1来引用接口方法第一个形参的值,使用?2来引用接口方法第二个形参的值,依次类推:

@Transactional
@Modifying
@Query(value="INSERT INTO t_user (username, passwd) VALUES (?1, ?2)", nativeQuery = true)
int save(String username, String passwd);

还可以使用命名参数来引用,在接口方法的形参加注解@Param传入参数的命名,在SQL语句中可以使用:paramName来引用:

@Query(value = "SELECT * FROM t_user WHERE id=:id", nativeQuery = true)
User findById(@Param("id")long id);

需要注意的坑:

1. 使用@Modifying标识的方法返回值只能是void、int/Integer。

2. 进行修改操作的时候需要开启事务,在方法上使用@Transactional来开启事务。

一个使用Query查询的例子:

package org.cc11001100.spring.data.jpa.dao;

import org.cc11001100.spring.data.jpa.domain.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Transactional; import java.util.List; public interface UserDao extends JpaRepository<User, Long> { @Transactional
@Modifying
@Query(value="INSERT INTO t_user (username, passwd) VALUES (?1, ?2)", nativeQuery = true)
int save(String username, String passwd); @Transactional
@Modifying
@Query(value = "DELETE FROM t_user WHERE id=?1", nativeQuery = true)
int deleteById(long id); @Transactional
@Modifying
@Query(value = "UPDATE t_user SET passwd=:passwd WHERE id=:id", nativeQuery = true)
int updatePasswd(@Param("id") long id, @Param("passwd") String passwd); @Query(value = "SELECT * FROM t_user WHERE id=:id", nativeQuery = true)
User findById(@Param("id")long id); @Query(value = "SELECT * FROM t_user", nativeQuery = true)
List<User> findAll(); }

命名查询(JPA NameQuery)

使用命名查询就是事先声明好SQL语句,给它一个名字,在使用的时候可以直接用过这个名字来使用它,故称之为命名查询。

使用自定义方法

当默认提供的方法不能满足要求的时候可能就需要自定义方法了,不过一般都是用不到的,Dao只需要简单的执行数据就可以了,与业务相关的放在Service层中。

事务

Spring Data JPA规定在进行写操作的时候必须要开启事务,在方法上使用@Transactional开启事务。

参考资料:

1. Spring Data JPA官方Quick Start http://projects.spring.io/spring-data-jpa/#quick-start

2. 官方文档(英文) http://docs.spring.io/spring-data/jpa/docs/current/reference/html/

3. IBM开发者文档 使用 Spring Data JPA 简化 JPA 开发 https://www.ibm.com/developerworks/cn/opensource/os-cn-spring-jpa/index.html

.

Spring Data JPA笔记的更多相关文章

  1. SpringBoot学习笔记:Spring Data Jpa的使用

    更多请关注公众号 Spring Data Jpa 简介 JPA JPA(Java Persistence API)意即Java持久化API,是Sun官方在JDK5.0后提出的Java持久化规范(JSR ...

  2. 干货|一文读懂 Spring Data Jpa!

    有很多读者留言希望松哥能好好聊聊 Spring Data Jpa!其实这个话题松哥以前零零散散的介绍过,在我的书里也有介绍过,但是在公众号中还没和大伙聊过,因此本文就和大家来仔细聊聊 Spring D ...

  3. 如何在Spring Data JPA中引入Querydsl

    一.环境说明 基础框架采用Spring Boot.Spring Data JPA.Hibernate.在动态查询中,有一种方式是采用Querydsl的方式. 二.具体配置 1.在pom.xml中,引入 ...

  4. SpringBoot系列之Spring Data Jpa集成教程

    SpringBoot系列之Spring Data Jpa集成教程 Spring Data Jpa是属于Spring Data的一个子项目,Spring data项目是一款集成了很多数据操作的项目,其下 ...

  5. 正确使用Spring Data JPA规范

    在优锐课的学习分享中探讨了关于,Spring Data JPA的创建主要是为了通过按方法名称生成查询来轻松创建查询. 但是,有时我们需要创建复杂的查询,而无法利用查询生成器.码了很多知识笔记分享给大家 ...

  6. 快速搭建springmvc+spring data jpa工程

    一.前言 这里简单讲述一下如何快速使用springmvc和spring data jpa搭建后台开发工程,并提供了一个简单的demo作为参考. 二.创建maven工程 http://www.cnblo ...

  7. spring boot(五):spring data jpa的使用

    在上篇文章springboot(二):web综合开发中简单介绍了一下spring data jpa的基础性使用,这篇文章将更加全面的介绍spring data jpa 常见用法以及注意事项 使用spr ...

  8. 转:使用 Spring Data JPA 简化 JPA 开发

    从一个简单的 JPA 示例开始 本文主要讲述 Spring Data JPA,但是为了不至于给 JPA 和 Spring 的初学者造成较大的学习曲线,我们首先从 JPA 开始,简单介绍一个 JPA 示 ...

  9. 深入浅出学Spring Data JPA

    第一章:Spring Data JPA入门 Spring Data是什么 Spring Data是一个用于简化数据库访问,并支持云服务的开源框架.其主要目标是使得对数据的访问变得方便快捷,并支持map ...

随机推荐

  1. docker 开启远程

    # vi /etc/init.d/docker   在start()中加入:$exec -H tcp://0.0.0.0:2375 -H unix://var/run/docker.sock -d & ...

  2. 【.Net】vs2017 自带发布工具 ClickOnce发布包遇到的问题

    一.遇到的问题 在安装了vs2017 社区版(Community)之后  想打包安装程序(winform) 还是想用之前的 installshield来打包  发现居然打不了,在官网查了    ins ...

  3. Graph-Based image segmentation method

    1.Graph-Based 方法简介 基于图的图像分割方法将图像伪造成带权值无向图的形式 : G = (V, E) 其中,V是顶点集合,把图像中的每个像素或者每个区域看成图的一个顶点:E是边的集合,连 ...

  4. bzoj1853-大包子的幸运数字

    题意 称只含有 6 和 8 的数字为幸运数字.称幸运数字的倍数为类幸运数字.求 \([l,r]\) 中有多少个类幸运数字.\(1\le l,r\le 10^{10}\) . 分析 幸运数字最多有 \( ...

  5. 【Mybatis】Mybatis的sql模糊查询

    这个网站中有很多方法.https://code.google.com/p/mybatis/issues/detail?id=85 自己试验了如下的方法. 1.  参数中直接加入%% param.set ...

  6. Unified Networking Lab 安装使用IOL镜像

    Unified Networking Lab 安装使用IOL镜像 Unified Networking Lab 很久以前,在一个星系远的地方,很远的工程师们为eBay寻找二手路由器来满足家庭实验的需求 ...

  7. 命令行下django-admin.py参数不起作用的问题解决

    django官方turial中创建本地web站点时,使用如下命令 django-admin.py startproject mysite 说一下我在使用时碰到的几个问题: 1.无法找到django-a ...

  8. QTREE6 - Query on a tree VI 解题报告

    QTREE6 - Query on a tree VI 题目描述 给你一棵\(n\)个点的树,编号\(1\)~\(n\).每个点可以是黑色,可以是白色.初始时所有点都是黑色.下面有两种操作请你操作给我 ...

  9. 监听input内容改变的oninput与onpropertychange在ie9的bug

    在做autocomplate的时候发现,ie9中,剪切.退格.删除不触发oninput事件,而ie9和ie9+已经移除了onpropertychange事件. 只好尝试添加退格.delete.剪切事件 ...

  10. 【bzoj4811】由乃的OJ

    Portal --> bzoj4811 Solution  这题可以用树剖+线段树做也可以用LCT做,不过大体思路是一样的  (接下来先讲的是树剖+线段树的做法,再提LCT的做法) ​  首先位 ...