SpringDataJpa2

1.SpringDataJpa的扩展 — 抽取

创建一个BaseRepository接口然后去继承JpaRepository和JpaSpecificationExecutor

然后在里面写我们自己想要的方法

在接口上边加上注解@NoRepositoryBean 这是为了防止底层去找SimpleJpaRepository的实现类 我们需要找我们自己的实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

* @NoRepositoryBean
* 防止底层去找SimpleJpaRepository的实现类 我们需要找我们自己的实现类
* */

public interface <T,ID extends Serializable> extends JpaRepository<T, ID>,
JpaSpecificationExecutor<T> {

//根据BaseQuery拿到分页对象
Page findPageByQuery(BaseQuery baseQuery);

//根据BaseQuery拿到对应的数据 不分页
List<T> findByQuery(BaseQuery baseQuery);

//根据jpql与对应的参数拿到数据
List findByJpql(String jpql,Object...value);

}

然后再创建一个实现类 实现我们刚写的BaseRepository接口

再继承 SimpleJpaRepository

这样就不用覆写SpringDataJpa本身的方法了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

* Serializable 序列化 用来网络传输
* extends SimpleJpaRepository 不会覆写SpringDataJpa本身的方法
* implements BaseRepository 实现自身的方法
*
*
* */
public class BaseRepositoryImpl<T,ID extends Serializable> extends SimpleJpaRepository<T,ID> implements <T,ID> {
//让Spring把entityManager注入进来
private EntityManager entityManager;

//继承了SimpleJpaRepository中没有无参构造 必须覆写里面的有参构造
public BaseRepositoryImpl(Class<T> domainClass, EntityManager em) {
super(domainClass, em);
this.entityManager = em;
}
//分页查询方法
@Override
public Page findPageByQuery(BaseQuery baseQuery) {
//排序
Sort sort = baseQuery.createSort();
//条件
Specification specs = baseQuery.createSpecifications();
//分页
Pageable pageable = new PageRequest(baseQuery.getCurrentPage(), baseQuery.getPageSize(), sort);
//查询
Page page = super.findAll(specs, pageable);
return page;
}

@Override
public List<T> findByQuery(BaseQuery baseQuery) {
Sort sort = baseQuery.createSort();
Specification spec = baseQuery.createSpecifications();
//条件加排序查询
List<T> list = super.findAll(spec, sort);
return list;
}

@Override
public List findByJpql(String jpql, Object... value) {
Query query = entityManager.createQuery(jpql);
for (int i = 0; i < value.length; i++) {
query.setParameter(i+1, value[i]);
}
return query.getResultList();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/**
* BaseRepositoryFactoryBean --FactoryBean
* JpaRepositoryFactoryBean --提供的FactoryBean
* 写一个类去继承 JpaRepositoryFactoryBean --Spring就会自动调用createRepositoryFactory
*
*/
public class BaseRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extends Serializable>
extends JpaRepositoryFactoryBean<T,S,ID> {

@Override
protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {
return new MyRepositoryFactory<T,ID>(entityManager); //注:这里创建是我们的自定义类
}

//继承JpaRepositoryFactory后,把返回的对象修改成我们自己的实现
private static class MyRepositoryFactory<T,ID extends Serializable> extends JpaRepositoryFactory {
private final EntityManager entityManager;
/**
* Creates a new {@link JpaRepositoryFactory}.
*
* @param entityManager must not be {@literal null}
*/
public MyRepositoryFactory(EntityManager entityManager) {
super(entityManager);
this.entityManager = entityManager;
}
//这里返回最后的功能对象
@Override
protected Object getTargetRepository(RepositoryInformation information) {
return new BaseRepositoryImpl<T,ID>((Class<T>)information.getDomainType(),entityManager);
}
//这里返回最后的功能对象 --最终Spring调用方法返回的对象 -->得到对最终的对象
//factoryBean 默认调用getObject -- 现在调用getTargetRepository
@Override
protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
return BaseRepositoryImpl.class;
}
}
}

​ 让Spring启动的时候,找BaseRepositoryImpl的实现 —>BaseRepositoryFactoryBean可以让spring去找

BaseRepositoryImpl这个实现

在applicationContext中增加

1
2
3
4
5
6
7
<!-- SpringDataJpa配置 -->
<jpa:repositories base-package="cn.itsource.repository"
transaction-manager-ref="transactionManager"
entity-manager-factory-ref="entityManagerFactory"
<!-- 加这一一个属性 -->
factory-class="cn.itsource.repository.impl.BaseRepositoryFactoryBean"
/>

2.抽取Service层

创建一个公共接口 IBaseService –> 完成CRUD以及三个公共方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public interface IBaseService<T,ID extends Serializable> {
//增加 修改
void save(T t);
//删除
void del(ID id);
//查询单个
T findOne(ID id);
//查询全部
List<T> findAll();
//分页排序查询
Page findPageByQuery(BaseQuery baseQuery);
//排序查询
List<T> findByQuery(BaseQuery baseQuery);
//jpql语句查询
List jpqlByQuery(String jpql,Object...value);
}

创建一个实现类BaseServiceImpl —> 实现IBaseService —> 实现所有方法 完成所有方法 加上事务管理注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
@Transactional(propagation = Propagation.SUPPORTS,readOnly = true)
public class BaseServiceImpl<T,ID extends Serializable> implements IBaseService<T, ID> {

@Autowired
private BaseRepository<T,ID> baseRepository;

@Override
@Transactional
public void save(T t) {
baseRepository.save(t);
}

@Override
@Transactional
public void del(ID id) {
baseRepository.delete(id);
}

@Override
@Transactional
public T findOne(ID id) {
return baseRepository.findOne(id);
}

@Override
public List<T> findAll() {
return baseRepository.findAll();
}

@Override
public Page findPageByQuery(BaseQuery baseQuery) {
return baseRepository.findPageByQuery(baseQuery);
}

@Override
public List<T> findByQuery(BaseQuery baseQuery) {
return baseRepository.findByQuery(baseQuery);
}

@Override
public List jpqlByQuery(String jpql, Object... value) {
return baseRepository.findByJpql(jpql, value);
}
}

​ 再创建IEmployeeService和EmployeeServiceImpl 分别继承接口和实现类 —> 继承之后就具备了CRUD以及公共的查询方法了

3.集成SpringMVC

(1) 导包

(2) 创建applicationContext-mvc配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<!-- 对静态资源进行放行 -->
<mvc:default-servlet-handler />
<!-- 扫描controller部分的包 -->
<!-- @Component组件, @Repository持久层, @Service业务逻辑层, and @Controller控制器 -->
<context:component-scan base-package="cn.itsource.web" />
<!-- 添加mvc对@RequestMapping等注解的支持 -->
<mvc:annotation-driven />

<!-- ViewResolver 视图解析器 (struts2视图类型类似) -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 设置视图路径的前后缀,该配置可以让我们写视图路径的时候更简单。 -->
<!-- 希望跳转jsp是[/WEB-INF/views/前缀][xxx变量][.jsp后缀] -->
<!-- * @see #setPrefix -->
<property name="prefix" value="/WEB-INF/views/" />
<!-- * @see #setSuffix -->
<property name="suffix" value=".jsp" />
</bean>

<!-- 错误:提示告诉开发者你没有配置文件上传解析器。 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 设置上传文件的最大尺寸为1MB -->
<property name="maxUploadSize">
<value>1048576</value>
</property>
</bean>

创建web.xml 进行配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<!-- 读取SpringMVC -->
<context-param>
<param-name>contextConfigLocation</param-name>
大专栏  SpringDataJpa2"> <param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!-- 启动Spring的监听器 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<!-- 配置解决中文乱码的问题 -->
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

<!-- 配置核心控制器-->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<!-- 告诉SpringMVC到哪里去找配置文件 -->
<param-name>contextConfigLocation</param-name>
<!-- 注意:这里只读取springmvc的xml -->
<param-value>classpath:applicationContext-mvc.xml</param-value>
</init-param>
<!-- Servlet默认在每一次访问的时候创建 -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

后面需要自己配置tomcat

4.加入Easyui

把easyui的文件复制到web下边

然后创建一个head.jsp 这个是专门方easyui引入的配置的

因为很多页面需要用到 所以单独抽取出来

1
2
3
4
5
6
7
8
9
10
<%@ page contentType="text/html;charset=UTF-8" language="java" %>

<link rel="stylesheet" type="text/css" href="/easyui/themes/default/easyui.css">
<link rel="stylesheet" type="text/css" href="/easyui/themes/icon.css">

<script type="text/javascript" src="/easyui/jquery.min.js"></script>
<script type="text/javascript" src="/easyui/jquery.easyui.min.js"></script>
<script type="text/javascript" src="/easyui/locale/easyui-lang-zh_CN.js"></script>
<script type="text/javascript" src="/easyui/plugin/jquery.jdirk.js"></script>
<script type="text/javascript" src="/js/common.js"></script>
1
2
3
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%-- 需要用到easyui引入 就加上这行代码 --%>
<%@include file="/WEB-INF/views/head.jsp"%>

然后就加入页面

树形菜单

1
2
3
4
5
6
7
8
9
10
//获取左边树状菜单栏
$("#menuTree").tree({
//获取json中的数据
url:"/json/menu.json",
//点击事件
onClick:function(node){
//增加页签 把名字和地址传过去
addTabs(node.text,node.url);
}
})

就是上边树形菜单点击事件触发之后调用下面的函数增加页签

tabs选项卡增加

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function addTabs(text,url) {
if(url){
if(!$("#dataTab").tabs("exists",text)){
var content = '<iframe scrolling="auto" frameborder="0" src="'+url+'" style="100%;height:100%;"></iframe>';
$("#dataTab").tabs("add",{
title:text,
content:content,
closable:true
});
}else{
$("#dataTab").tabs("select",text);
}
}
}

5.分页

因为json中分页需要total row

1564844780728

所以我们创建一个公共类

类里面有这两个字段 rows是显示在页面的 数据 所以用List

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class UIPage {
//总条数
private Long total;
//每页显示数据
private List rows;

public UIPage() {}

public UIPage(Page page) {
this.total = page.getTotalElements();
this.rows = page.getContent();
}
//下面有get/set方法
}

在controller中 把分页查询出来的数据放到UIPage中去

1
2
3
4
5
6
7
@RequestMapping("/page")
@ResponseBody
public UIPage page(EmployeeQuery employeeQuery){
Page page = employeeService.findPageByQuery(employeeQuery);
UIPage uiPage = new UIPage(page);
return uiPage;
}

​ 前台加载数据的时候 接受的参数用EmployeeQuery 但是需要在BaseQuery中设置set方法 把前台加载的页数和页面展示条数传到BaseQuery

1
2
3
4
5
6
7
8
//因为前台传入的参数是page和rows
public void setPage(Integer page){
System.out.println(page);
this.currentPage = page;
}
public void setRows(Integer rows){
this.pageSize = rows;
}

前台传的参数

1564845295829

6.头像和部门

在头像和部门的标签里加上 formatter (单元格formatter(格式化器)函数)

1
2
<th width="20"  field="headImage" formatter="headImage" >头像</th>
<th width="20" field="department" formatter="department" >部门</th>

然后创建在js文件夹中创建一个model文件夹 里面创建一个employee的js文件

1
2
3
4
5
6
7
8
9
10
function department(value) {
if(value){
return value.name;
}
}
function headImage(value) {
if(value){
return "<img src='"+value+"' />";
}
}

7.解决懒加载问题

在wei.xml里面加

1
2
3
4
5
6
7
8
9
<!-- 加上OpenEntityManager -->
<filter>
<filter-name>openEntity</filter-name>
<filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>openEntity</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

方法一:在实体类建立关系的上面加上注解

1
2
3
4
@ManyToOne
@JoinColumn(name = "department_id")
@JsonIgnoreProperties(value={"hibernateLazyInitializer","handler","fieldHandler"})
private Department department;

方法二:因为方法一只能对单个有效 有很多类都需要的时候 很麻烦

​ (1) 创建一个新的类(重写com.fasterxml.jackson.databind.ObjectMapper)

1
2
3
4
5
6
7
public class CustomMapper extends ObjectMapper {
public CustomMapper(){
this.setSerializationInclusion(JsonInclude.Include.NON_NULL);
// 设置 SerializationFeature.FAIL_ON_EMPTY_BEANS 为 false 对null的bean 不做序列化
this.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
}
}

(2) 在applicationContext-mvc.xml中配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!-- 添加mvc对@RequestMapping等注解的支持 -->
<mvc:annotation-driven >
<mvc:message-converters>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>application/json; charset=UTF-8</value>
<value>application/x-www-form-urlencoded; charset=UTF-8</value>
</list>
</property>
<!-- No serializer:配置 objectMapper 为我们自定义扩展后的 CustomMapper,解决了返回对象有关系对象的报错问题 -->
<property name="objectMapper">
<!-- 这里路径根据自己的路径改 -->
<bean class="cn.itsource.common.CustomMapper"></bean>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>

8.高级查询

页面准备好之后 需要回显部门下拉框

先去repositroy去写一个IDepartmentRepository接口

再去service把IDepartmentService接口和实现类写好

再创建一个UtilController

1
2
3
4
5
6
7
8
9
10
11
12
13
@Controller
@RequestMapping("/util")
public class UtilController {
@Autowired
private IDepartmentService departmentService;

@RequestMapping("/departmentList")
@ResponseBody
public List<Department> departmentList(){
List<Department> list = departmentService.findAll();
return list;
}
}

页面准备for表单 点击搜索的时候 加载数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$(function () {
var searchForm = $("#searchForm");
var employeeGrid = $("#employeeGrid");

//获取带有data-method属性的a标签 然后绑定事件
$("a[data-method]").on("click",function () {
var methodName = $(this).data("method");
itsource[methodName]();
})

var itsource = {
search:function () {
var parme = searchForm.serializeObject();
employeeGrid.datagrid("load",parme);
}
}
})

在EmployeeQuery增加几个属性和条件

1
2
3
4
5
6
7
8
public Specification createSpecifications() {
Specification<Employee> specification = Specifications.<Employee>and()
.like(StringUtils.isNotBlank(this.username), "username", "%" + this.username + "%")
.like(StringUtils.isNotBlank(this.email), "email", "%" + this.email + "%")
.eq(this.departmentId != null,"department.id",this.departmentId)
.build();
return specification;
}
原文地址:https://www.cnblogs.com/lijianming180/p/12360771.html