您的位置:首页 > 编程语言 > Java开发

springMVC+hibernate+springdata+querydsl搭建框架(优缺点分析+springdata介绍)

2016-08-03 22:56 477 查看
参考资料:

springdata:http://docs.spring.io/spring-data/data-jpa/docs/current/reference/html/#jpa.query-methods.at-query

querydsl:http://www.querydsl.com/static/querydsl/3.4.3/reference/html_single/

简介:springdata(以下简称data) 是spring家族的一大开源框架,其主要目的是规范数据持久化层,目的在于消除不同数据库(NOSQL和关系型数据库)之间的矛盾,提供了一套DAO层简化开发接口(即用户使用时,只需要新建一个接口继承data提供的一套接口(CrudRepository ,PagingAndSortingRepository …),data就会自动帮你自动生成并注入dao实体对象,几乎能完成所有的简单的业务逻辑,效果相似mybatis

为什么要选择这样的框架搭建模式?

1.利用data的优点,简化dao层开发,开发时只需要写dao接口,便可以完成开发,加快开发速度

例子:

/**
* @author jiangjintai
*
*/
public interface AdminDao extends JpaRepository<TbAdmin, Integer>,
QueryDslPredicateExecutor<TbAdmin> {

}


ps:相比也与baseDao的开发方法,他的优点在于面向接口编程

2.利用hibernate的ORM思想,配合data,这里不再累述

3.复杂查询交给querydsl处理,摆脱sql语言,java里面不再有sql语言的存在(除非非常特殊情况)

缺点:

1.影响性能,把工作都交给了spring处理。

2.优化方面难以提升,该框架封装了所有的sql语言,我们无法从sql语言方面进行优化。

spring data 功能详解

1.data 提供了一个整个框架主要的核心接口 Repository,根据该接口,data实现了一个领域类去管理所有的dao领域模型,根据该超级接口,衍生了CrudRepository接口,该接口主要负责普通的crud操作:

public interface CrudRepository<T, ID extends Serializable>
extends Repository<T, ID> {

<S extends T> S save(S entity);
//增
T findOne(ID primaryKey);
//查
Iterable<T> findAll();
//查全部
Long count();
//查全部数量
void delete(T entity);
//删除
boolean exists(ID primaryKey);
//判断是否存在
// … more functionality omitted.
}


基于该接口,又衍生出了JpaRepositoryMongoRepository,等接口,主要面向与不同的持久化层。

除了普通的curb操作,数据分页,排序,对我们来说也是经常用到的逻辑,记得读者以前不懂该框架时,自己又没封装,每次分页都是自己用hql写的查询。现在PagingAndSortingRepository可以帮助我们解决这些难题。

public interface PagingAndSortingRepository<T, ID extends Serializable>
extends CrudRepository<T, ID> {

Iterable<T> findAll(Sort sort);

Page<T> findAll(Pageable pageable);
}

//这里的Page是data对结果集进行封装,包括
//1.结果List
//2.当前页面和总页面
//3.当前每页数据量
//4.当前全部页面数据总量
//5.上一页下一页的数据转换
//相关数据data不会查询好了给你,是延时加载形式


可以看到,接口方法提供了一个Pageable参数,该参数即是让你用来设置你要查询的结果集的数量和页面数,使用方法

Pageable pageable = new PageRequest(0,20);//从0开始查询二十条


有了页面查询外,PageRequest还提供另一个构造方法,让使用者可以使用他进行排序

new PageRequest(0, 20, new Sort(Direction.DESC,"id"))
//Sort是一个排序对象,让使用者可以根据entity的某个属性或多个属性进行排序
//Direction是一个枚举,里面只提供两个排序,升序ASC,降序DESC


2.查询方法

当你发现JpaRepository(继承CrubRepository和PagingAndSortingRepository)里面提供的方法不够用时,你可以通过这样的方式命名自己的方法,data将会检查你的方法名,然后根据您的方法名进行处理

public TbUser findByUserName(String userName);
//注意,这里的findBy是固定的,后面接的属性名一定要和该entity一致,不然会报出没有该属性错误
//特别注意,传入参数的类型,一定一定要和entity的属性类型一样,不然也会报错,即使是Integer和int也要分清楚,作者就在这里吃了很多亏,data不会帮你自动解包装包


补充一下,如果使用了hibernate 的实体对象,如想要查询

user.address.name属性时

直接使用

public TbUser findByTbAddressName(String name)//进行查询

//有时候我们的一级实体类里面有同名属性,假设有一个属性就叫TbAddressName时


我们可以使用

public TbUser findByTbUserTbAddress_Name(String name);//进行区分


//根据页面页码查询
public Page<TbUser>  findByUserName(String userName,Pageable pageable);


//特殊删除
public Long deleteByUserName(String userName);


当然,也包含很多逻辑处理方法







3.data还有适应于其他持久化层,作者还未设计过NOSQL数据库,在这里就不做详述,具体可查询api。

4.data还支持,屏蔽CrubRepository接口的方法,这里作者也不做累述,具体实现思路是

4.1新建一个接口继续data接口,加上@NoRepositoryBean,覆盖你需要的接口,使用该接口作为你项目的超级接口

5.使用@query做特殊查询

当你遇到特殊的情况,需要sql语句进行解决时,你可以使用一下啊的方法进行查询

@Query("select u from User u")
Stream<User> findAllByCustomQueryAndStream();

Stream<User> readAllByFirstnameNotNull();

@Query("select u from User u")
Stream<User> streamAllPaged(Pageable pageable);
//这里返回的时候一个流对象,当你需要使用时,你需要使用


try (Stream<User> stream = repository.findAllByCustomQueryAndStream()) {
stream.forEach(…);
}//方法进行遍历,回到原始状态


这里也可以采用hibernate里面的具名查询方式,具体如下

@Entity
@NamedQuery(name = "User.findByEmailAddress",
query = "select u from User u where u.emailAddress = ?1")
public class User {

}


public interface UserRepository extends JpaRepository<User, Long> {

List<User> findByLastname(String lastname);

User findByEmailAddress(String emailAddress);
}


使用@query查询时会先对@query里面的sql|hql语句进行查询,方法命名可以随意命名并不会报错

public interface UserRepository extends JpaRepository<User, Long> {

@Query("select u from User u where u.emailAddress = ?1")
//这里的参数是从1开始的不是从0开始的
User findByEmailAddress(String emailAddress);
}


public interface UserRepository extends JpaRepository<User, Long> {

@Query("select u from User u where u.firstname like %?1")
List<User> findByFirstnameEndsWith(String firstname);
}


使用原生sql查询

public interface UserRepository extends JpaRepository<User, Long> {

@Query(value = "SELECT * FROM USERS WHERE EMAIL_ADDRESS = ?1", nativeQuery = true)//加上标志
User findByEmailAddress(String emailAddress);
}


//这里也可以配置Pageable查询

public interface UserRepository extends JpaRepository<User, Long> {

@Query(value = "SELECT * FROM USERS WHERE LASTNAME = ?1",
countQuery = "SELECT count(*) FROM USERS WHERE LASTNAME = ?1",
nativeQuery = true)
Page<User> findByLastname(String lastname, Pageable pageable);
}


可以传入具名参数

public interface UserRepository extends JpaRepository<User, Long> {

@Query("select u from User u where u.firstname = :firstname or u.lastname = :lastname")
User findByLastnameOrFirstname(@Param("lastname") String lastname,
@Param("firstname") String firstname);
}


6.当你需要在dao接口处理特殊的逻辑时你可以写一个自己的接口,然后在同 java文件下使用一个类去继承该接口,然后打上@NoRepositoryBean标志,再用自己最终的dao接口去继承,便可以实现接口的个性化拓展

7.data对于querydsl的拓展

Querydsl 是对sql语言静态结构化的一个框架,使用其api,可以进行SQL相似查询。

data提供接口QueryDslPredicateExecutor

public interface QueryDslPredicateExecutor<T> {

T findOne(Predicate predicate);

Iterable<T> findAll(Predicate predicate);

long count(Predicate predicate);

boolean exists(Predicate predicate);

// … more functionality omitted.
}

//Predicate 类是指使用querydsl 后的得到的断言,把断言传给data,data就能按你想要的结果进行逻辑运算。

//在这里顺便提醒一下,data使用的api是com.querydsl.*系列包
//而不是com.mysema.querydsl.*系列包,主要不要混淆,作者就在这里吃了很大的亏,两包都是由同一间公司维护开发,但是是因为包名不同,所以不能整合,调试了很久才发现是细节上的错误


下面给出一个查询的例子

Predicate predicate = user.firstname.equalsIgnoreCase("dave")
.and(user.lastname.startsWithIgnoreCase("mathews"));
userRepository.findAll(predicate);

//这里是querydsl查询,具体调用方式作者会在下篇文章说明,或者查阅querydsl相关说明


如果我们使用了data对querydsl的支持,我们将使用java模拟sql语句书写我们的特殊查询方式,帮助我们克服特殊的查询

8.data对springMVC的支持

data提供了一系列IOC 对springmvc 请求的支持,在这里,需要特殊说明一下,官方文档上面提到,要拓展data对springMVC的支持要使用两种办法

8.1 在配置java文件类上面标注

@EnableWebMvc

@EnableSpringDataWebSupport

8.2在xml文件里面注入

<bean class="org.springframework.data.web.config.SpringDataWebConfiguration" />


但是作者两者都试过了都没办法使之与mvc整合,最后得通过mvc的参数解析器配置才能解决问题,具体如下:

<!-- springdata参数解析器  -->
<mvc:annotation-driven>
<mvc:argument-resolvers>
<ref bean="sortResolver"/>
<ref bean="pageableResolver" />
</mvc:argument-resolvers>
</mvc:annotation-driven>

<bean id="sortResolver" class="org.springframework.data.web.SortHandlerMethodArgumentResolver" />
<bean id="pageableResolver" class="org.springframework.data.web.PageableHandlerMethodArgumentResolver">
<constructor-arg ref="sortResolver" />
</bean>


这里给出一个例子

@Controller
@RequestMapping("/users")
public class UserController {

@Autowired UserRepository repository;

@RequestMapping
public String showUsers(Model model, Pageable pageable) {

model.addAttribute("users", repository.findAll(pageable));
return "users";
}
}

//看到这里,我们可以把Pageable 作为参数直接传送给控制器方法,此时,我们只需要按照data给的参数规范,springmvc将会自动帮助我们注入规范如下


1.page:获取页码0为起始点

2.size:每页数据总数

3.sort:使用 属性,ASC|DESC 格式决定排序 例如:?sort=firstname&sort=lastname,asc

20170209更 注意:如果这里想使用自定义默认参数时,要使用@PageableDefault对pageable进行定义,如以下格式:

@PageableDefault(sort = { “id” }, direction =Sort.Direction.DESC)

Pageable pageable

不能够用@RequestParam(default=”id,desc”),如果使用默认参数的话,spring不会自动注入到pageable里面

4.当你遇到有两个以上页面选项的时候,你可以使用

public String showUsers(Model model,
@Qualifier("foo") Pageable first,
@Qualifier("bar") Pageable second) { … }


方法区分不同的页面,传参时,在上述四个点前面修改

如页面数:

foo_page 和 bar_page 使用下划线隔开分别

在这里,data还提供了一个PagedResourcesAssembler类可以对page进行封装解析

@Controller
class PersonController {

@Autowired PersonRepository repository;

@RequestMapping(value = "/persons", method = RequestMethod.GET)
HttpEntity<PagedResources<Person>> persons(Pageable pageable,
PagedResourcesAssembler assembler) {

Page<Person> persons = repository.findAll(pageable);
return new ResponseEntity<>(assembler.toResources(persons), HttpStatus.OK);
}
}


他将会把page里面的结果自动进行处理,封装后的结果例子如下:

{ "links" : [ { "rel" : "next",
"href" : "http://localhost:8080/persons?page=1&size=20 }
],
"content" : [
… // 20 Person instances rendered here
],
"pageMetadata" : {
"size" : 20,
"totalElements" : 30,
"totalPages" : 2,
"number" : 0
}
}


9.还可以使用querydsl在mvc参数中进行注入,较为复杂,不做详解

10.data对Specifications的支持

Specifications类似于hibernate的QBC查询,使用时需要自己进行封装,比较麻烦,这里不做详解,当然网上也有开源的封装代码,大家有兴趣可以进行了解

springdata 功能非常强大,这里只是介绍小部分,大家有兴趣可以去参考官方文档,一个帮助大家快速开发的框架

————–2016年8月11号更新——————-

在1.2版本好像不支持下面的语法

在写这篇文章是用1.10

可以使用Top和first进行结果集限制

User findFirstByOrderByLastnameAsc();

User findTopByOrderByAgeDesc();

Page<User> queryFirst10ByLastname(String lastname, Pageable pageable);

Slice<User> findTop3ByLastname(String lastname, Pageable pageable);

List<User> findFirst10ByLastname(String lastname, Sort sort);

List<User> findTop10ByLastname(String lastname, Pageable pageable);


————————–2016年8月31更新———————–

count查询

public interface UserRepository extends CrudRepository<User, Long> {

Long countByLastname(String lastname);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息