Spring data简介:

Spring Data是一个用于简化数据库访问,并支持云服务的开源框架。其主要目标是使得对数据的访问变得方便快捷,并支持map-reduce框架和云计算数据服务。 Spring Data 包含多个子项目:

Commons - 提供共享的基础框架,适合各个子项目使用,支持跨数据库持久化

JPA - 简化创建 JPA 数据访问层和跨存储的持久层功能

Hadoop - 基于 Spring 的 Hadoop 作业配置和一个 POJO 编程模型的 MapReduce 作业

Key-Value  - 集成了 Redis 和 Riak ,提供多个常用场景下的简单封装

Document - 集成文档数据库:CouchDB 和 MongoDB 并提供基本的配置映射和资料库支持

Graph - 集成 Neo4j 提供强大的基于 POJO 的编程模型

Graph Roo AddOn - Roo support for Neo4j

JDBC Extensions - 支持 Oracle RAD、高级队列和高级数据类型

Mapping - 基于 Grails 的提供对象映射框架,支持不同的数据库

Examples - 示例程序、文档和图数据库

Guidance - 高级文档

一、Spring data JPA简介

Spring data JPA是Spring在ORM框架,以及JPA规范的基础上,封装的一套JPA应用框架,并提供了一整套的数据访问层解决方案。

二、Spring data JPA的功能

Spring data JPA的功能非常的强大,这里我们先跳过环境搭建这一步,来一睹Spring data JPA的“芳容”。

Spring data JPA提供给用户使用的,主要有以下几个接口:

Repository:仅仅是一个标识,表明任何继承它的均为仓库接口类,方便Spring自动扫描识别 
CrudRepository:继承Repository,实现了一组CRUD相关的方法 
PagingAndSortingRepository:继承CrudRepository,实现了一组分页排序相关的方法 
JpaRepository:继承PagingAndSortingRepository,实现一组JPA规范相关的方法 
JpaSpecificationExecutor:比较特殊,不属于Repository体系,实现一组JPA Criteria查询相关的方法。

三、Spring data JPA的接口

1、CrudRepository接口

建立一个Entity类:

@Entity
@Table(name="USER")
public class User {
    @Id
    @GeneratedValue
    private Integer id;
    
    //账号
    private String account;
    
    //姓名
    private String name;
    
    //密码
    private String password;
    
    // 邮箱
    private String email;
}
编写接口,并继承CrudRepository接口:
public interface UserRepository extends CrudRepository<User, Integer> {
    
}
编写测试类(为了更直观的看到效果,所有测试类都没有使用断言,直接使用的打印语句):
public class UserRepositoryTest {
    @Autowired
    private UserRepository dao;
    
    @Test//保存
    public void testSave(){
        User user = new User();
        user.setName("chhliu");
        user.setAccount("10000");
        user.setEmail("chhliu@.com");
        user.setPassword("123456");
        dao.save(user);
    }
    
    @Test//批量保存
    public void testSave1(){
        List<User> users = new ArrayList<User>();
        User user = new User();
        user.setName("tanjie");
        user.setAccount("10000");
        user.setEmail("tanjie@.com");
        user.setPassword("123456");
        users.add(user);
        user = new User();
        user.setName("esdong");
        user.setAccount("10000");
        user.setEmail("esdong@.com");
        user.setPassword("123456");
        users.add(user);
        user = new User();
        user.setName("qinhongfei");
        user.setAccount("10000");
        user.setEmail("qinhongfei@.com");
        user.setPassword("123456");
        users.add(user);
        user = new User();
        user.setName("huizhang");
        user.setAccount("10000");
        user.setEmail("huizhang@.com");
        user.setPassword("123456");
        users.add(user);
        user = new User();
        user.setName("caican");
        user.setAccount("10000");
        user.setEmail("caican@.com");
        user.setPassword("123456");
        users.add(user);
        dao.save(users);
    }
    
    @Test//更新
    public void testUpdate(){
        User user = dao.findOne(1);
        user.setPassword("123890");// 要想这样实现更新的功能,需要在service层加上@Transaction事物注解
    }
    
    @Test//删除
    public void testDelete(){
        dao.delete(2);
    }
    
    @Test//查询所有
    public void testFindAll(){
        List<User> users = (List<User>) dao.findAll();
        System.out.println(JSON.toJSONString(users));
    }
    
    @Test//判断指定的id对象是否存在
    public void testIsExist(){
        boolean isExist = dao.exists(8);
        System.out.println(isExist);
    }
    
    @Test//通过id列表来查询
    public void testFindUserByIds(){
        List<Integer> listIds = new ArrayList<Integer>();
        listIds.add(2);
        listIds.add(4);
        listIds.add(7);
        List<User> users = (List<User>) dao.findAll(listIds);
        System.out.println(JSON.toJSONString(users));
    }
}
大家可以看出,到这里,我就只写了一个接口类,并没有实现这个接口类,就可以完成基本的CRUD操作。因为这个接口会自动为域对象创建增删改查方法,供业务层直接使用。
该接口的定义如下,总共提供了11个方法,基本上可以满足简单的CRUD操作以及批量操作:

@NoRepositoryBean
public interface CrudRepository<T, ID extends Serializable> extends Repository<T, ID> {
    <S extends T> S save(S entity);//保存
    <S extends T> Iterable<S> save(Iterable<S> entities);//批量保存
    T findOne(ID id);//根据id查询一个对象
    boolean exists(ID id);//判断对象是否存在
    Iterable<T> findAll();//查询所有的对象
    Iterable<T> findAll(Iterable<ID> ids);//根据id列表查询所有的对象
    long count();//计算对象的总个数
    void delete(ID id);//根据id删除
    void delete(T entity);//删除对象
    void delete(Iterable<? extends T> entities);//批量删除
    void deleteAll();//删除所有
}
2、PagingAndSortingRepository接口
PagingAndSortingRepository接口继承了CrudRepository接口。

编写接口,并继承PagingAndSortingRepository接口

public interface UserRepositoryWithOrder extends
        PagingAndSortingRepository<User, Integer> {
 
}
编写测试类:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:applicationContext-config.xml" })
@TransactionConfiguration(defaultRollback = false)
@Transactional
public class UserRepositoryWithOrderTest {
    @Autowired
    private UserRepositoryWithOrder dao;
    
    @Test
    public void testOrder(){
        Sort sort = new Sort(Direction.DESC, "id");
        Pageable pageable = new PageRequest(0, 5, sort);
        Page<User> page = dao.findAll(pageable);
        System.out.println(JSON.toJSONString(page));
        System.out.println(page.getSize());
    }
}
只要继承了这个接口,Spring data JPA就已经为你提供了分页和排序的功能了。该接口的定义如下,主要提供了两个方法,供使用,其中T是要操作的实体类,ID是实体类主键的类型
@NoRepositoryBean
public interface PagingAndSortingRepository<T, ID extends Serializable> extends CrudRepository<T, ID> {
    Iterable<T> findAll(Sort sort);// 不带分页的排序
    Page<T> findAll(Pageable pageable);// 带分页的排序
}
3、JpaRepository接口
如果业务需要即提供CRUD操作,又需要提供分页以及排序功能,那么就可以直接继承这个接口。该接口继承了PagingAndSortingRepository接口。

接口定义如下:

public interface JpaRepository<T, ID extends Serializable> extends PagingAndSortingRepository<T, ID> {
    List<T> findAll();//查询所有对象,不排序
    List<T> findAll(Sort sort);//查询所有对象,并排序
    <S extends T> List<S> save(Iterable<S> entities);//批量保存
    void flush();//强制缓存与数据库同步
    T saveAndFlush(T entity);//保存并强制同步
    void deleteInBatch(Iterable<T> entities);//批量删除
    void deleteAllInBatch();//删除所有
}
4、JpaSpecificationExecutor接口
该接口提供了对JPA Criteria查询的支持。注意,这个接口很特殊,不属于Repository体系,而Spring data JPA不会自动扫描识别,所以会报找不到对应的Bean,我们只需要继承任意一个继承了Repository的子接口或直接继承Repository接口,Spring data JPA就会自动扫描识别,进行统一的管理。

编写接口如下:

public interface SpecificationExecutorRepository extends CrudRepository<User, Integer>,
        JpaSpecificationExecutor<User> {
 
}
Service类:
@Service
public class SpecificationExecutorRepositoryManager {
    @Autowired
    private SpecificationExecutorRepository dao;
    /**
     * 描述:根据name来查询用户
     */
    public User findUserByName(final String name){
        return dao.findOne(new Specification<User>() {
            
            @Override
            public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query,
                    CriteriaBuilder cb) {
                Predicate predicate = cb.equal(root.get("name"), name);
                return predicate;
            }
        });
    }
    
    /**
     * 描述:根据name和email来查询用户
     */
    public User findUserByNameAndEmail(final String name, final String email){
        return dao.findOne(new Specification<User>() {
            
            @Override
            public Predicate toPredicate(Root<User> root,
                    CriteriaQuery<?> query, CriteriaBuilder cb) {
                List<Predicate> list = new ArrayList<Predicate>();
                Predicate predicate1 = cb.equal(root.get("name"), name);
                Predicate predicate2 = cb.equal(root.get("email"), email);
                list.add(predicate1);
                list.add(predicate2);
                // 注意此处的处理
                Predicate[] p = new Predicate[list.size()];
                return cb.and(list.toArray(p));
            }
        });
    }
    
    /**
     * 描述:组合查询
     */
    public User findUserByUser(final User userVo){
        return dao.findOne(new Specification<User>() {
            
            @Override
            public Predicate toPredicate(Root<User> root,
                    CriteriaQuery<?> query, CriteriaBuilder cb) {
                Predicate predicate = cb.equal(root.get("name"), userVo.getName());
                cb.and(predicate, cb.equal(root.get("email"), userVo.getEmail()));
                cb.and(predicate, cb.equal(root.get("password"), userVo.getPassword()));
                return predicate;
            }
        });
    }
    
    /**
     * 描述:范围查询in方法,例如查询用户id在[2,10]中的用户
     */
    public List<User> findUserByIds(final List<Integer> ids){
        return dao.findAll(new Specification<User>() {
 
            @Override
            public Predicate toPredicate(Root<User> root,
                    CriteriaQuery<?> query, CriteriaBuilder cb) {
                return root.in(ids);
            }
        });
    }
    
    /**
     * 描述:范围查询gt方法,例如查询用户id大于9的所有用户
     */
    public List<User> findUserByGtId(final int id){
        return dao.findAll(new Specification<User>() {
 
            @Override
            public Predicate toPredicate(Root<User> root,
                    CriteriaQuery<?> query, CriteriaBuilder cb) {
                return cb.gt(root.get("id").as(Integer.class), id);
            }
        });
    }
    
    /**
     * 描述:范围查询lt方法,例如查询用户id小于10的用户
     */
    public List<User> findUserByLtId(final int id){
        return dao.findAll(new Specification<User>() {
 
            @Override
            public Predicate toPredicate(Root<User> root,
                    CriteriaQuery<?> query, CriteriaBuilder cb) {
                return cb.lt(root.get("id").as(Integer.class), id);
            }
        });
    }
    
    /**
     * 描述:范围查询between方法,例如查询id在3和10之间的用户
     */
    public List<User> findUserBetweenId(final int start, final int end){
        return dao.findAll(new Specification<User>() {
 
            @Override
            public Predicate toPredicate(Root<User> root,
                    CriteriaQuery<?> query, CriteriaBuilder cb) {
                return cb.between(root.get("id").as(Integer.class), start, end);
            }
        });
    }
    
    /**
     * 描述:排序和分页操作
     */
    public Page<User> findUserAndOrder(final int id){
        Sort sort = new Sort(Direction.DESC, "id");
        return dao.findAll(new Specification<User>() {
 
            @Override
            public Predicate toPredicate(Root<User> root,
                    CriteriaQuery<?> query, CriteriaBuilder cb) {
                return cb.gt(root.get("id").as(Integer.class), id);
            }
        }, new PageRequest(0, 5, sort));
    }
    
    /**
     * 描述:只有排序操作
     */
    public List<User> findUserAndOrderSecondMethod(final int id){
        return dao.findAll(new Specification<User>() {
 
            @Override
            public Predicate toPredicate(Root<User> root,
                    CriteriaQuery<?> query, CriteriaBuilder cb) {
                cb.gt(root.get("id").as(Integer.class), id);
                query.orderBy(cb.desc(root.get("id").as(Integer.class)));
                return query.getRestriction();
            }
        });
    }
}
测试类:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:applicationContext-config.xml" })
@TransactionConfiguration(defaultRollback = false)
@Transactional
public class SpecificationExecutorRepositoryManagerTest {
    @Autowired
    private SpecificationExecutorRepositoryManager manager;
    @Test
    public void testFindUserByName(){
        User user = manager.findUserByName("chhliu");
        System.out.println(JSON.toJSONString(user));
    }
    
    @Test
    public void testFindUserByNameAndEmail(){
        User user = manager.findUserByNameAndEmail("chhliu", "chhliu@.com");
        System.out.println(JSON.toJSONString(user));
    }
    
    @Test
    public void testFindUserByUserVo(){
        User user = new User();
        user.setName("chhliu");
        user.setEmail("chhliu@.com");
        User u = manager.findUserByUser(user);
        System.out.println(JSON.toJSONString(u));
    }
    
    @Test
    public void testFindUserByIds(){
        List<User> users = manager.findUserByIds(new ArrayList<Integer>(Arrays.asList(1,3,5,6)));
        System.out.println(JSON.toJSONString(users));
    }
    
    @Test
    public void testFindUserByGtId(){
        List<User> users = manager.findUserByGtId(5);
        System.out.println(JSON.toJSONString(users));
    }
    
    @Test
    public void testFindUserByLtId(){
        List<User> users = manager.findUserByLtId(5);
        System.out.println(JSON.toJSONString(users));
    }
    
    @Test
    public void testFindUserBetweenId(){
        List<User> users = manager.findUserBetweenId(4, 9);
        System.out.println(JSON.toJSONString(users));
    }
    
    @Test
    public void testFindUserAndOrder(){
        Page<User> users = manager.findUserAndOrder(1);
        System.out.println(JSON.toJSONString(users));
    }
    
    @Test
    public void testFindUserAndOrderSecondMethod(){
        List<User> users = manager.findUserAndOrderSecondMethod(1);
        System.out.println(JSON.toJSONString(users));
    }
}
5、Repository接口
这个接口是最基础的接口,只是一个标志性的接口,没有定义任何的方法,那这个接口有什么用了?既然Spring data JPA提供了这个接口,自然是有它的用处,例如,我们有一部分方法是不想对外提供的,比如我们只想提供增加和修改方法,不提供删除方法,那么前面的几个接口都是做不到的,这个时候,我们就可以继承这个接口,然后将CrudRepository接口里面相应的方法拷贝到Repository接口就可以了。

总结:上述五个接口,开发者到底该如何选择?其实依据很简单,根据具体的业务需求,选择其中之一。因为各个接口之间并不存在功能强弱的问题。

四、Spring data JPA的查询

1、使用 @Query 创建查询

@Query 注解的使用非常简单,只需在声明的方法上面标注该注解,同时提供一个 JP QL 查询语句即可。很多开发者在创建 JP QL 时喜欢使用命名参数来代替位置编号,@Query 也对此提供了支持。JP QL 语句中通过": 变量"的格式来指定参数,同时在方法的参数前面使用 @Param 将方法参数与 JP QL 中的命名参数对应。此外,开发者也可以通过使用 @Query 来执行一个更新操作,为此,我们需要在使用 @Query 的同时,用 @Modifying 来将该操作标识为修改查询,这样框架最终会生成一个更新的操作,而非查询操作。

编写接口,如下:

/**
 * 描述:自定义查询,当Spring Data JPA无法提供时,需要自定义接口,此时可以使用这种方式
 */
public interface UserDefineBySelf extends JpaRepository<User, Integer> {
    /**
     * 命名参数
     * 描述:推荐使用这种方法,可以不用管参数的位置
     */
    @Query("select u from User u where u.name = :name")
    User findUserByName(@Param("name") String name);
    
    /**
     * 索引参数
     * 描述:使用?占位符
     */
    @Query("select u from User u where u.email = ?1")// 1表示第一个参数
    User findUserByEmail(String email);
    
    /**
     * 描述:可以通过@Modifying和@Query来实现更新
     * 注意:Modifying queries的返回值只能为void或者是int/Integer
     */
    @Modifying
    @Query("update User u set u.name = :name where u.id = :id")
    int updateUserById(@Param("name") String name, @Param("id") int id);
}
注:@Modifying注解里面有一个配置clearAutomatically
它说的是可以清除底层持久化上下文,就是entityManager这个类,我们知道jpa底层实现会有二级缓存,也就是在更新完数据库后,如果后面去用这个对象,你再去查这个对象,这个对象是在一级缓存,但是并没有跟数据库同步,这个时候用clearAutomatically=true,就会刷新hibernate的一级缓存了, 不然你在同一接口中,更新一个对象,接着查询这个对象,那么你查出来的这个对象还是之前的没有更新之前的状态
测试类:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:applicationContext-config.xml" })
@TransactionConfiguration(defaultRollback = false)
@Transactional
public class UserDefineBySelfTest {
    @Autowired
    private UserDefineBySelf dao;
    
    @Test
    public void testFindUserByName(){
        User user = dao.findUserByName("chhliu");
        Assert.assertEquals("chhliu", user.getName());
        System.out.println(user.getName());
    }
    
    @Test
    public void testFindUserByEmail(){
        User user = dao.findUserByEmail("chhliu@.com");
        Assert.assertEquals("chhliu", user.getName());
        System.out.println(user.getName());
    }
    
    @Test
    public void testUpdateUserById(){
        dao.updateUserById("tanjie", 4);
    }
}
从测试代码可以看出,我们同样只定义了接口,没有任何的实现类,但是却实现了我们所需要的功能。
2、使用@NamedQueries创建查询

命名查询是 JPA 提供的一种将查询语句从方法体中独立出来,以供多个方法共用的功能。Spring Data JPA 对命名查询也提供了很好的支持。用户只需要按照 JPA 规范在 orm.xml 文件或者在代码中使用 @NamedQuery(或 @NamedNativeQuery)定义好查询语句,唯一要做的就是为该语句命名时,需要满足”DomainClass.methodName()”的 命名规则。

编写接口:

public interface FindUserByNamedQueryRepository extends JpaRepository<User, Integer> {
    User findUserWithName(@Param("name") String name);
}
编写类:
@Entity
@NamedQueries(value={
        @NamedQuery(name="User.findUserWithName",query="select u from User u where u.name = :name")
})
// 注意:此处如果是多个方法,那么需要使用@NamedQueries,如果只有一个方法,则可以使用@NamedQuery,写法如下:@NamedQuery(name="User.findUserWithName",query="select u from User u where u.name = :name")
public class FindUserByNamedQuery {
    /**
     * 注意:此处必须要给这个实体类定义一个唯一标识,否则会报异常
     */
    @Id
    @GeneratedValue
    private Integer id;
}
注意:文中标记为红色的部分,需要一一对应,否则不满足JPA 的规范。
测试类:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:applicationContext-config.xml" })
@TransactionConfiguration(defaultRollback = false)
@Transactional
public class FindUserByNamedQueryRepositoryTest {
    @Autowired
    private FindUserByNamedQueryRepository dao;
    
    @Test
    public void testFindUserByName(){
        User user = dao.findUserWithName("caican");
        System.out.println(JSON.toJSONString(user));
    }
}
3、通过解析方法名创建查询
顾名思义,就是根据方法的名字,就能创建查询,也许初听起来,感觉很不可思议,等测试后才发现,原来一切皆有可能。

编写接口:

public interface SimpleConditionQueryRepository extends JpaRepository<User, Integer> {
    /**
     * 说明:按照Spring data 定义的规则,查询方法以find|read|get开头
     * 涉及条件查询时,条件的属性用条件关键字连接,要注意的是:条件属性首字母需大写
     */
    
    
    
    /**
     * 注:此处这个接口相当于发送了一条SQL:select u from User u where u.name = :name and u.email = :email
     * 参数名大写,条件名首字母大写,并且接口名中参数出现的顺序必须和参数列表中的参数顺序一致
     */
    User findByNameAndEmail(String name, String email);
    
    /**
     * 注:此处这个接口相当于发送了一条SQL:select u from User u where u.name = ?1 or u.password = ?2
     */
    List<User> findByNameOrPassword(String name, String password);
    
    /**
     * 注:此处这个接口相当于发送了一条SQL:select u from User u where u.id between ?1 and ?2
     */
    List<User> findByIdBetween(Integer start, Integer end);
    
    /**
     * 注:此处这个接口相当于发送了一条SQL:select u from User u where u.id < ?1
     */
    List<User> findByIdLessThan(Integer end);
    
    /**
     * 注:此处这个接口相当于发送了一条SQL:select u from User u where u.id > ?1
     */
    List<User> findByIdGreaterThan(Integer start);
    
    /**
     * 注:此处这个接口相当于发送了一条SQL:select u from User u where u.name is null
     */
    List<User> findByNameIsNull();
    
    /**
     * 注:此处这个接口相当于发送了一条SQL:select u from User u where u.name is not null
     */
    List<User> findByNameIsNotNull();
    
    /**
     * 注:此处这个接口相当于发送了一条SQL:select u from User u where u.name like ?1
     */
    List<User> findByNameLike(String name);
    
    /**
     * 注:此处这个接口相当于发送了一条SQL:select u from User u where u.name not like ?1
     */
    List<User> findByNameNotLike(String name);
    
    /**
     * 注:此处这个接口相当于发送了一条SQL:select u from User u where u.password = ?1 order by u.id desc
     */
    List<User> findByPasswordOrderByIdDesc(String password);
    
    /**
     * 注:此处这个接口相当于发送了一条SQL:select u from User u where u.name <> ?1
     */
    List<User> findByNameNot(String name);
    
    /**
     * 注:此处这个接口相当于发送了一条SQL:select u from User u where u.id in ?1
     */
    List<User> findByIdIn(List<Integer> ids);
    
    /**
     * 注:此处这个接口相当于发送了一条SQL:select u from User u where u.id not in ?1
     */
    List<User> findByIdNotIn(List<Integer> ids);
}
测试类(注释部分为实际发送的sql语句):
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:applicationContext-config.xml" })
@TransactionConfiguration(defaultRollback = false)
@Transactional
public class SimpleConditionQueryRepositoryTest {
    @Autowired
    private SimpleConditionQueryRepository dao;
    
    /**
     * select
        user0_.id as id0_,
        user0_.account as account0_,
        user0_.email as email0_,
        user0_.name as name0_,
        user0_.password as password0_ 
    from
        USER user0_ 
    where
        user0_.name=? 
        and user0_.email=? limit ?
     */
    @Test
    public void testFindUserByNameAndEmail(){
        User user = dao.findByNameAndEmail("chhliu", "chhliu@.com");
        System.out.println(JSON.toJSONString(user));
    }
    
    /**
     * select
        user0_.id as id1_,
        user0_.account as account1_,
        user0_.email as email1_,
        user0_.name as name1_,
        user0_.password as password1_ 
    from
        USER user0_ 
    where
        user0_.name=? 
        or user0_.password=?
     */
    @Test
    public void testFindUserByNameOrPassword(){
        List<User> users = dao.findByNameOrPassword("chhliu", "123456");
        System.out.println(JSON.toJSONString(users));
    }
    
    /**
     * select
        user0_.id as id1_,
        user0_.account as account1_,
        user0_.email as email1_,
        user0_.name as name1_,
        user0_.password as password1_ 
    from
        USER user0_ 
    where
        user0_.id between ? and ?
     */
    @Test
    public void testFindByIdBetween(){
        List<User> users = dao.findByIdBetween(5, 8);
        System.out.println(JSON.toJSONString(users));
    }
    
    /**
     * select
        user0_.id as id1_,
        user0_.account as account1_,
        user0_.email as email1_,
        user0_.name as name1_,
        user0_.password as password1_ 
    from
        USER user0_ 
    where
        user0_.id<?
     */
    @Test
    public void testFindByIdLessThan(){
        List<User> users = dao.findByIdLessThan(4);
        System.out.println(JSON.toJSONString(users));
    }
    
    /**
     * select
        user0_.id as id0_,
        user0_.account as account0_,
        user0_.email as email0_,
        user0_.name as name0_,
        user0_.password as password0_ 
    from
        USER user0_ 
    where
        user0_.id>?
     */
    @Test
    public void testFindByIdGreaterThan(){
        List<User> users = dao.findByIdGreaterThan(6);
        System.out.println(JSON.toJSONString(users));
    }
    
    /**
     * select
        user0_.id as id0_,
        user0_.account as account0_,
        user0_.email as email0_,
        user0_.name as name0_,
        user0_.password as password0_ 
    from
        USER user0_ 
    where
        user0_.name is null
     */
    @Test
    public void testFindByNameIsNull(){
        List<User> users = dao.findByNameIsNull();
        System.out.println(JSON.toJSONString(users));
    }
    
    /**
     * select
        user0_.id as id1_,
        user0_.account as account1_,
        user0_.email as email1_,
        user0_.name as name1_,
        user0_.password as password1_ 
    from
        USER user0_ 
    where
        user0_.name is not null
     */
    @Test
    public void testFindByNameIsNotNull(){
        List<User> users = dao.findByNameIsNotNull();
        System.out.println(JSON.toJSONString(users));
    }
    
    /**
     * select
        user0_.id as id1_,
        user0_.account as account1_,
        user0_.email as email1_,
        user0_.name as name1_,
        user0_.password as password1_ 
    from
        USER user0_ 
    where
        user0_.name like ?
     */
    @Test
    public void testFindByNameLike(){
        List<User> users = dao.findByNameLike("chhliu");
        System.out.println(JSON.toJSONString(users));
    }
    
    /**
     * select
        user0_.id as id0_,
        user0_.account as account0_,
        user0_.email as email0_,
        user0_.name as name0_,
        user0_.password as password0_ 
    from
        USER user0_ 
    where
        user0_.name not like ?
     */
    @Test
    public void testFindByNameNotLike(){
        List<User> users = dao.findByNameNotLike("chhliu");
        System.out.println(JSON.toJSONString(users));
    }
    
    /**
     * select
        user0_.id as id0_,
        user0_.account as account0_,
        user0_.email as email0_,
        user0_.name as name0_,
        user0_.password as password0_ 
    from
        USER user0_ 
    where
        user0_.password=? 
    order by
        user0_.id desc
     */
    @Test
    public void testFindByPasswordOrderByIdDesc(){
        List<User> users = dao.findByPasswordOrderByIdDesc("123456");
        System.out.println(JSON.toJSONString(users));
    }
    
    /**
     * select
        user0_.id as id1_,
        user0_.account as account1_,
        user0_.email as email1_,
        user0_.name as name1_,
        user0_.password as password1_ 
    from
        USER user0_ 
    where
        user0_.name<>?
     */
    @Test
    public void testFindByNameNot(){
        List<User> users = dao.findByNameNot("chhliu");
        System.out.println(JSON.toJSONString(users));
    }
    
    /**
     * select
        user0_.id as id1_,
        user0_.account as account1_,
        user0_.email as email1_,
        user0_.name as name1_,
        user0_.password as password1_ 
    from
        USER user0_ 
    where
        user0_.id in (
            ? , ? , ? , ?
        )
     */
    @Test
    public void testFindByIdIn(){
        List<User> users = dao.findByIdIn(new ArrayList<Integer>(Arrays.asList(3,4,6,8)));
        System.out.println(JSON.toJSONString(users));
    }
    
    /**
     * select
        user0_.id as id0_,
        user0_.account as account0_,
        user0_.email as email0_,
        user0_.name as name0_,
        user0_.password as password0_ 
    from
        USER user0_ 
    where
        user0_.id not in  (
            ? , ? , ? , ?
        )
     */
    @Test
    public void testFindByIdNotIn(){
        List<User> users = dao.findByIdNotIn(new ArrayList<Integer>(Arrays.asList(3,4,6,8)));
        System.out.println(JSON.toJSONString(users));
    }
}
这里,我们只定义了一个接口,接口里面只有方法,但是没有任何的实现,却完成了各种操作。
看到这里,估计很多人都会问,Spring data JPA是怎么做到的了?原来,框架在进行方法名解析时,会先把方法名多余的前缀截取掉,比如 find、findBy、read、readBy、get、getBy,然后对剩下部分进行解析。并且如果方法的最后一个参数是 Sort 或者 Pageable 类型,也会提取相关的信息,以便按规则进行排序或者分页查询。在创建查询时,我们通过在方法名中使用属性名称来表达,比如 findByIdIn()。框架在解析该方法时,首先剔除 findBy,然后对剩下的属性进行解析。

在查询时,通常需要同时根据多个属性进行查询,且查询的条件也格式各样(大于某个值、在某个范围等等),Spring Data JPA 为此提供了一些表达条件查询的关键字,大致如下:

And --- 等价于 SQL 中的 and 关键字,比如 findByUsernameAndPassword(String user, Striang pwd)

Or --- 等价于 SQL 中的 or 关键字,比如 findByUsernameOrAddress(String user, String addr)

Between --- 等价于 SQL 中的 between 关键字,比如 findBySalaryBetween(int max, int min)

LessThan --- 等价于 SQL 中的 "<",比如 findBySalaryLessThan(int max)

GreaterThan --- 等价于 SQL 中的">",比如 findBySalaryGreaterThan(int min)

IsNull --- 等价于 SQL 中的 "is null",比如 findByUsernameIsNull()

IsNotNull --- 等价于 SQL 中的 "is not null",比如 findByUsernameIsNotNull()

NotNull --- 与 IsNotNull 等价

Like --- 等价于 SQL 中的 "like",比如 findByUsernameLike(String user)

NotLike --- 等价于 SQL 中的 "not like",比如 findByUsernameNotLike(String user)

OrderBy ---等价于 SQL 中的 "order by",比如 findByUsernameOrderBySalaryAsc(String user)

Not --- 等价于 SQL 中的 "! =",比如 findByUsernameNot(String user)

In --- 等价于 SQL 中的 "in",比如 findByUsernameIn(Collection<String> userList) ,方法的参数可以是 Collection 类型,也可以是数组或者不定长参数

NotIn --- 等价于 SQL 中的 "not in",比如 findByUsernameNotIn(Collection<String> userList) ,方法的参数可以是 Collection 类型,也可以是数组或者不定长参数

五、创建查询的顺序

Spring Data JPA 在为接口创建代理对象时,如果发现同时存在多种上述情况可用,它该优先采用哪种策略呢?为此,<jpa:repositories> 提供了 query-lookup-strategy 属性,用以指定查找的顺序。它有如下三个取值:

create --- 通过解析方法名字来创建查询。即使有符合的命名查询,或者方法通过 @Query 指定的查询语句,都将会被忽略。

create-if-not-found --- 如果方法通过 @Query 指定了查询语句,则使用该语句实现查询;如果没有,则查找是否定义了符合条件的命名查询,如果找到,则使用该命名查询;如果两者都没有找到,则通过解析方 法名字来创建查询。这是 query-lookup-strategy 属性的默认值。

use-declared-query --- 如果方法通过 @Query 指定了查询语句,则使用该语句实现查询;如果没有,则查找是否定义了符合条件的命名查询,如果找到,则使用该命名查询;如果两者都没有找到,则抛出异常。

六、Spring Data JPA 对事务的支持

细心的读者也许从上面的代码中看出了一些端倪,我们在使用Spring data JPA的时候,只是定义了接口,在使用的时候,直接注入就可以了,并没有做与事物相关的任何处理,但实际上,事物已经起到效果了,这又是为什么了?

默认情况下,Spring Data JPA 实现的方法都是使用事务的。针对查询类型的方法,其等价于 @Transactional(readOnly=true);增删改类型的方法,等价于 @Transactional。可以看出,除了将查询的方法设为只读事务外,其他事务属性均采用默认值。

如果用户觉得有必要,可以在接口方法上使用 @Transactional 显式指定事务属性,该值覆盖 Spring Data JPA 提供的默认值。同时,开发者也可以在业务层方法上使用 @Transactional 指定事务属性,这主要针对一个业务层方法多次调用持久层方法的情况。持久层的事务会根据设置的事务传播行为来决定是挂起业务层事务还是加入业务层的事务。

spring data jpa使用的几种方式相关推荐

  1. springdatajpa命名规则_简单了解下spring data jpa

    公司准备搭建一个通用框架,以后项目就用统一一套框架了 以前只是听过jpa,但是没有实际用过 今天就来学习下一些简单的知识 什么是JPA 全称Java Persistence API,可以通过注解或者X ...

  2. Spring Data JPA教程

    在Java类或对象与关系数据库之间管理数据是一项非常繁琐且棘手的任务. DAO层通常包含许多样板代码,应简化这些样板代码,以减少代码行数并使代码可重复使用. 在本教程中,我们将讨论Spring数据的J ...

  3. 学习Spring Data JPA

    简介 Spring Data 是spring的一个子项目,在官网上是这样解释的: Spring Data 是为数据访问提供一种熟悉且一致的基于Spring的编程模型,同时仍然保留底层数据存储的特​​殊 ...

  4. spring data jpa封装specification实现简单风格的动态查询

    github:https://github.com/peterowang/spring-data-jpa-demo 单一实体的动态查询: @Servicepublic class AdvancedUs ...

  5. ORM框架之Spring Data JPA(二)spring data jpa方式的基础增删改查

    上一篇主要在介绍hibernate实现jpa规范,如何实现数据增删改查,这一篇将会着重spring data jpa 一.Spring Data JPA 1.1 Spring Data JPA介绍: ...

  6. Spring Boot整合Spring Data JPA操作数据

    一. Sping Data JPA 简介 Spring Data JPA 是 Spring 基于 ORM 框架.JPA 规范的基础上封装的一套 JPA 应用框架,底层使用了 Hibernate 的 J ...

  7. Spring Data JPA(官方文档翻译)

    关于本书 介绍 关于这本指南 第一章 前言 第二章 新增及注意点 第三章 项目依赖 第四章 使用Spring Data Repositories 4.1 核心概念 4.2 查询方法 4.3 定义rep ...

  8. Spring Data JPA

    1.    概述 Spring JPA通过为用户统一创建和销毁EntityManager,进行事务管理,简化JPA的配置等使用户的开发更加简便. Spring Data JPA是在Spring JPA ...

  9. spring data jpa从入门到精通_Spring Data JPA的简单入门

    前言 spring data JPA是spring团队打造的sping生态全家桶的一部分,本身内核使用的是hibernate核心源码,用来作为了解java持久层框架基本构成的样本是再好不过的选择.最近 ...

最新文章

  1. 2020年SWPUACM团队新生第四次周赛(题解)
  2. android 原始定位,安卓原生定位
  3. hat怎么安装mysql_Red Hat Enterprise Linux中怎么安装Mysql+apache+php+zend
  4. 多表关系介绍 mysql
  5. 洛阳计算机学校排名2015年,洛阳最好的中专学校有哪些 十大中专学校排名
  6. STM32工作笔记0052---串口通信原理--UART
  7. 动态人脸检测(脸数可调)
  8. java-序列化以及反序列化
  9. 聚集索引,非聚集索引,覆盖索引 原理
  10. 网络通信优化之通信协议:如何优化RPC网络通信?
  11. AirSim学习日志 9-三维空间航路点跟踪
  12. [unix] Unix 是简单的,你不需要成为一个天才或是计算机专家也能理解它!
  13. arm64_linux head.S的执行流程(3.18)- 12.msm8996 地址空间布局
  14. win10更新后 chrome内核浏览器总是打开网页一直加载 甚至打不开 解决方法
  15. Kafka结合Spark-streaming 的两种连接方式(AWL与直连)
  16. JavaScript使用url字符串拼接传递参数,截取参数
  17. chmod 权限777 -rwxrwxrwx是什么意思
  18. 赋能IT,创新价值——互联网时代企业IT转型实践
  19. httpd模块支持https请求模块mod_ssl
  20. USB Type-C简介

热门文章

  1. DVWA靶场Brute Force 暴力破解审计通关教程
  2. c语言的37个关键字,c语言的37个关键字都是什么
  3. 端午节来点烟花庆祝庆祝
  4. 「湖南周边游」安化云台山风景区——适合亲子游的地方
  5. ubuntu kernel编译
  6. 原理图端口符号_原理图输入设计历史(一)
  7. 【C语言】帮你解决c语言中数组的问题
  8. iview 表单验证问题 Select 已经选择 还是弹验证提示
  9. 工作一年了,跟领导提工资发的邮件
  10. 给定一个非负整数num,范围中的每个数字i 计算其二进制中1的个数