Java for Web学习笔记(一一四):Spring Data(2)仓库接口

查询方法

CrudRepository中只定义了通过主键查询的方式,这显然不能满足需求。Spring提供了查询方法来解决这样问题。查询方法包括:

  • find...By
  • get...By
  • read...By

将返回单个结果T,或者多个结果Iterable<T>, List<T>, Collection<T>,Page<T>。下面是例子:

public interface BookRepository extends PagingAndSortingRepository<Book, Long>{
    // 在PagingAndSortingRepository外增加三个查询方法,对应UNIQUE KEY和KEY的查询。无需编写任何实现代码,Spring data会自动实现的。
    Book findByIsbn(String isbn);
    List<Book> findByAuthor(String author);
    List<Book> findByPublisher(String publisher);  
    // find...By,By后面的是条件,而find和By之前一般无特定意义,除非是SQL语句中的特定含义 
    // 中间的Book无意义,只是给方法名字更好的语义,等同于findByIsbn()
    Book findBookByIsbn(String isbn); 
    // 这里面的distinct作为保留字有意义,Books无意义,相当于SELECT DISTICT * FROM Book Where Publisher=?,
    // 假定entity映射的表格为Book,属性publisher对应的列名了Publisher
    List<Book> findDistinctBooksByPublisher(String publisher); 
}
作者和出版商都可能有很多的书,需要进行分页处理,我们改为:
public interface BookRepository extends PagingAndSortingRepository<Book, Long>{
    Book findByIsbn(String isbn);
    Page<Book> findByAuthor(String author, Pageable instructions);
    Page<Book> findByPublisher(String publisher, Pageable instructions);
}
上面例子中Entity的属性都是简单类型,也可以是复杂类型,例如PersonEntity中含有Address address,而Address又具有若干属性。我们同样可以通过前面介绍的方式匹配具体的Address,但是如果我们只需要匹配Address里面的某个属性,例如邮编。可以写为:
List<Person> findByAddressPostalCode(PostalCode code); //匹配address属性里面的postalCode属性,这种写法容易引起歧义,最好采用下划线方式
Page<Person> findByAddress_PostalCode(PostalCode code, Pageable instructions);
By后面放置查询条件,我们具体了解如何设置:
// ➤ By后面缺省是等于,也就是Is或者Equal,下面等同与Book findByIsbn(String isbn); 
Book findByIsbnIs(String isbn);
Book findByIsbnEqual(String isbn);
// ➤ NOT,IsNOT表示不等于 
List<Book> findByIsbnIsNot(Strign isbn);
List<Book> findByIsbnIsNotEqual(Strign isbn);
// ➤ OR 和 AND的条件组合 
List<Book> findByAuthorAndPublisher(String author, String publisher);
List<Book> findByAuthorOrPublisher(String author, String publisher);
// ➤ 大小写不敏感:数据库中有些列的匹配已经是设定为忽略大小写,我们也可以在方法中明确声明。但从查询效率,我们应答在表格设计中体现,例如某个KEY是大小写不敏感的。 
//只是publisher忽略大小写
List<Book> findByAuthorAndPublisherIgnoreCase(String author, String publisher); 
//author和publisher忽略大小写
List<Book> findByAuthorIgnoreCaseAndPublisherIgnoreCase(String author, String publisher); 
//所有均忽略大小写
List<Book> findByAuthorAndPublisherAllIgnoreCase(String author, String publisher); 
// ➤ After, IsAfter,Befor,IsBefor用于对时间或日期的操作  
List<Book> findByDateFoundedIsAfter(Date date);
// ➤ 对于%的处理:Contains,Containing, IsContaining,StartsWith,StartingWith, IsStartingWith,EndsWith,EndingWith, IsEndingWith  
// 下面相当于WHERE title = '%value%' 
List<Book> findByTitleContains(String value);
// ➤ Like: 下面相当于WHERE title = 'value',注意这和Contain等很相似,但是通配符%要放入到value之中 
List<Book> findByTitleLike(String value);
// ➤ Between,IsBetween 
List<Book> findByDateFoundedBetween(Date start, Date end);
// ➤ Exists:相当于SQL中的EXISTS 
// ➤ True,IsTrue,False,IsFalse:对boolean的属性的检查 
List<Book> findByApprovedIsFalse();
// ➤ GreaterThan,IsGreaterThan,GreaterThanEqual,IsGreaterThanEqual,LessThan,IsLessThan,LessThanEqual,IsLessThanEqual
// ➤ In:表示属性的值必须等于里面的某个值,必须采用Iterable来表述 
List<Book> findByAuthorIn(Iterable<String> authors);
// ➤ Null,IsNull:值为null,方法不应带有参数,应为已经对值表述清楚 
// ➤ Near, IsNear, Within, IsWithin常用于NoSQL,不在JPA中使用 
// ➤ Regex,MatchesRegex,Matches用于正则表达式 
List<Book> findByTitleRegex(String regex);

自定义的方法

Spring data很强大,但不一定能完全覆盖我们的需要,在某些情况,我们需要自定义方法。

为某个仓库提供自定义方法

1、我们定义某个仓库接口,在里面提供定义自定义的方法。

public interface TestRepositoryCustom {
	List<TestEntity> test(int page, int perPageNum, Predicate ... restrictions);
}

2、Spring Data的仓库接口将extends含有自定义的方法的接口

public interface TestRepository extends CrudRepository<TestEntity, Long>,TestRepositoryCustom{
	TestEntity findByUserName(String userName);
}

3、实现自定义的方法。注意,如果这个类无需加上@Repository,但是类名必须为Spring data仓库接口加上Impl,Spring data扫描到TestRepository后,将会在同一package下面查找TestRepositoryImpl,如有,将作为Spring bean具体实例化(也就是为何我们无需加上@Repository),我们应在此具体实现自定义的方法。至于自定义的接口的名字无任何要求。

public class TestRepositoryImpl implements TestRepositoryCustom{
	@PersistenceContext private EntityManager entityManager;

	@Override
	public List<TestPage> test(int page, int numPerPage, Predicate ... restrictions) {
		// 具体的代码 ......
	}
}

为所有仓库提供自定义方法

这应属于很罕见的情况,我们需要为所有的仓库都提供同样的自定义方法。

1、自定义的接口。这里extends了JpaRepository(属于Spring data,其extends了CrudRepository和PagingAndSortingRepository),以便Spring data可以扫描到。标记@NoRepositoryBean是告知Spring data不要为这个接口生成一个实现,因为只是我们通用的自定义接口,不对应具体的仓库。

@NoRepositoryBean
public interface CustomRepository<T, ID extends Serializable> extends JpaRepository<T, ID>{
    public void customOperation(T entity);
}

2、提供实现,我们必须继承Spring data JAP提供的基础仓库类SimpleJpaRepository。SimpleJpaRepository提供了预定义的接口方法,如findOne(ID),save(T)。如果需要提供Querydsl,则应extends替换为QueryDslJpaRepository。

public class CustomRepositoryImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID> 
 implements CustomRepository<T, ID>{
    private Class<T> domainClass;
    private EntityManager entityManager;

    public CustomRepositoryImpl(Class<T> domainClass, EntityManager entityManager){
        super(domainClass, entityManager);
        this.domainClass = domainClass;
        this.entityManager = entityManager;
    }

    public CustomRepositoryImpl(JpaEntityInformation<T, ?> information,EntityManager entityManager){
        super(information, entityManager);
        this.domainClass = information.getJavaType();
        this.entityManager = entityManager;
    }

    public void customOperation(T){
        // code to implement custom operation
    }
}

3、让所有仓库支持这些自定义的方法。由于我们标记了@NoRepositoryBean,Spring Data JPA不会自动扫描CustomRepositoryImpl,需要创建一个factory bean替代缺省的factory bean来完成这项工作,让扫描仓库接口后,不将仓库接口+Impl作为其实现,而指定为CustomRepositoryImpl。

public class CustomRepositoryFactoryBean<R extends JpaRepository<T, ID>, T,ID extends Serializable>
 extends JpaRepositoryFactoryBean<R, T, ID>{
    @Override
    protected RepositoryFactorySupport createRepositoryFactory(EntityManager e){
        return new CustomRepositoryFactory<T, ID>(e);
    }

    private static class CustomRepositoryFactory<T, ID extends Serializable> extends JpaRepositoryFactory{
        private EntityManager entityManager;

        public CustomRepositoryFactory(EntityManager entityManager){
            super(entityManager);
            this.entityManager = entityManager;
        }

        @Override
        @SuppressWarnings("unchecked")
        protected Object getTargetRepository(RepositoryMetadata metadata){
            return new CustomRepositoryImpl<T, ID>((Class<T>) metadata.getDomainType(), this.entityManager);
        }

        @Override
        protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata){
            return CustomRepositoryImpl.class;
        }
    }
}
4、将缺省的JpaRepositoryFactoryBean修改为CustomRepositoryFactoryBean
@EnableJpaRepositories(basePackages = "cn.wei.flowingflying.chapter22.site.repositories",
    //Returns the FactoryBean class to be used for each repository instance. Defaults to JpaRepositoryFactoryBean.
    repositoryFactoryBeanClass = CustomRepositoryFactoryBean.class)


相关链接:我的Professional Java for Web Applications相关文章

猜你喜欢

转载自blog.csdn.net/flowingflying/article/details/79515078