1.repository层(dao层)
①创建BaseRepository父接口
- 只要该接口被扫描到,Spring就回去管理它,
- 会为该接口创建一个实现类用于crud,但是类型不确定,
- 不知道做谁的crud,所以不让Spring创建该接口实现类就行了 @NoRepositoryBean
- 可以参照JpaRepository接口源码,写泛型
@NoRepositoryBean
public interface BaseRepository<T,ID extends Serializable> extends JpaRepository<T,ID>,JpaSpecificationExecutor<T> {
}
②EmployeeRepository继承父接口
public interface EmployeeRepository extends BaseRepository<Employee,Long>{
...
}
2.service层
①IBaseService父接口
创建service层父接口IBaseService,泛型和repository层(保持一致)
/*
* 泛型和repository层(保持一致)
* */
public interface IBaseService<T,ID extends Serializable> {
//添加和修改
void save(T t);
//删除
void delete(ID id);
//查询一条
T findOne(ID id);
//查询所有
List<T> findAll();
//根据查询对象查询
List<T> queryAll(BaseQuery baseQuery);
//根据查询对象拿到分页数据
Page<T> queryPageAndSort(BaseQuery baseQuery);
//jpql查询 可能有多个参数,数量不确定
List<Object> queryJpql(String jpql,Object...params);
}
②IEmployeeService子接口
IEmployeeService子接口继承父接口IBaseService
/*
* 目前没有内容,以后会写内容
* */
public interface IEmployeeService extends IBaseService<Employee,Long>{
}
③BaseServiceImpl父接口实现类
创建BaseServiceImpl父接口实现类,实现crud的方法
- 父接口的实现,不用创建对象(abstract修饰)
- 注意加事务@Transactional
- 注入EntityManager对象
@PersistenceContext
private EntityManager entityManager;
/*
* 父接口的实现,所以不用创建对象(abstract修饰 定义抽象类) 也不确定类型
* */
@Transactional(readOnly = true,propagation = Propagation.SUPPORTS)//一般查询多
public abstract class BaseServiceImpl<T,ID extends Serializable> implements IBaseService<T,ID> {
/*
* 这里接受的是BaseRepository的子实现类对象
* 子实现类有很多种,根据BaseServiceImpl的子类实现确定类型,从而确定了BaseRepository<T,ID>的接受类型
* */
@Autowired
private BaseRepository<T,ID> baseRepository;
@PersistenceContext
private EntityManager entityManager;
@Override
@Transactional
public void save(T t) {
baseRepository.save(t);
}
@Override
@Transactional
public void delete(ID id) {
baseRepository.delete(id);
}
@Override
public T findOne(ID id) {
return baseRepository.findOne(id);
}
@Override
public List<T> findAll() {
return baseRepository.findAll();
}
@Override
public List<T> queryAll(BaseQuery baseQuery) {
//拿到规则
Specification spec = baseQuery.createSpec();
return baseRepository.findAll(spec);
}
@Override
public Page<T> queryPageAndSort(BaseQuery baseQuery) {
//创建排序对象
Sort sort = baseQuery.createSort();
//创建分页对象
Pageable pageable = new PageRequest(baseQuery.getJpaCurrentPage(),baseQuery.getPageSize(),sort);
//拿到规则
Specification spec = baseQuery.createSpec();
//查询
return baseRepository.findAll(spec,pageable);
}
@Override
public List<Object> queryJpql(String jpql, Object... params) {
//创建查询对象
Query query = entityManager.createQuery(jpql);
//设置参数
for (int i = 0;i<params.length;i++) {
query.setParameter(i+1,params[i]);
}
//获取结果
return query.getResultList();
}
}
图例:
④EmployeeServiceImpl子接口实现类
继承父接口实现类同时实现子接口
@Service
public class EmployeeServiceImpl extends BaseServiceImpl<Employee,Long> implements IEmployeeService{
}
3.controller层(Springmvc集成)
SpringMVC集成步骤:
①applicationContext-mvc.xml配置
- 扫描包
- 静态资源放行
- mvc注解支持
- 视图解析器
- 上传解析器 id名称必须是 multipartResolver
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
">
<!--扫描包-->
<context:component-scan base-package="com.luo.aisell.controller"/>
<!--静态资源放行-->
<mvc:default-servlet-handler/>
<!--注解支持-->
<mvc:annotation-driven/>
<!--视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--前缀-->
<property name="prefix" value="/WEB-INF/views/"/>
<!--后缀-->
<property name="suffix" value=".jsp"/>
</bean>
<!--上传解析器 id名称必须是 multipartResolver-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize">
<value>10485760</value>
</property>
</bean>
</beans>
②web.xml配置
版本必须大于2.4
- 运行与读取spring的配置
- 配置mvc的核心控制器
- 读取mvc配置
- tomcat启动创建
- 编码过滤
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
id="WebApp_ID" version="3.1">
<!--SpringMVC的配置和Spring的配置要单独读取,否则后面集成其它框架会出问题-->
<!--运行与读取spring的配置-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!--配置mvc的核心控制器-->
<servlet>
<servlet-name>dispatchServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--读取mvc配置-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext-mvc.xml</param-value>
</init-param>
<!--tomcat启动用创建-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatchServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--编码过滤-->
<filter>
<filter-name>encodingFilter</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>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
③创建BaseController父类
public class BaseController {
}
④创建EmployeeController子类
测试controller是否可以访问前台
@Controller
@RequestMapping("/employee")
public class EmployeeController extends BaseController{
@Autowired
private IEmployeeService employeeService;
@RequestMapping("/index")
public String index(){
return "employee/index";
}
@RequestMapping("/list")
@ResponseBody
public List<Employee> list(){
EmployeeQuery employeeQuery = new EmployeeQuery();
return employeeService.queryAll(employeeQuery);
}
@RequestMapping("/page")
@ResponseBody
public UIPage page(EmployeeQuery query){
//返回UIPage对象
return new UIPage(employeeService.queryPageAndSort(query));
}
/*
* 删除后会返回一个json数据{success:true/false,msg:xxx}
* */
@RequestMapping("/delete")
@ResponseBody
public JpaResult delete(Long id){
try {
employeeService.delete(id);
//成功
return new JpaResult();
} catch (Exception e) {
e.printStackTrace();
//失败
return new JpaResult(false,e.getMessage());
}
}
}
4.集成easyui
- 将esayui文件夹拷贝到webapp目录下
- 在WEB-INF下创建views/employee/index.jsp结构
- views下创建head.jsp头文件
①创建头文件head.jsp
每个使用easyui的界面都需要引入很多文件,添麻烦,抽取一个公共的头文件head.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%--css样式--%>
<link rel="stylesheet" type="text/css" href="/easyui/themes/default/easyui.css">
<%--图标样式--%>
<link rel="stylesheet" type="text/css" href="/easyui/themes/icon.css">
<%--jQuery支持--%>
<script type="text/javascript" src="/easyui/jquery.min.js"></script>
<%--扩展包--%>
<script type="text/javascript" src="/easyui/plugin/jquery.jdirk.js"></script>
<%--easyui核心文件--%>
<script type="text/javascript" src="/easyui/jquery.easyui.min.js"></script>
<%--国际化--%>
<script type="text/javascript" src="/easyui/locale/easyui-lang-zh_CN.js"></script>
在每个jsp页面文件中引入head.jsp就可以使用easyui了
<%--引入头部文件--%>
<%@include file="/WEB-INF/views/head.jsp"%>
②为每个模块创建各自的js文件
结构如下:employee为例
里面写该模块的js代码
5.分页查询实现
①index.jsp添加数据表格
- fit:true自适应
- pagination:true添加分页工具栏
<%--表格--%>
<table id="datagrid" class="easyui-datagrid"
data-options="url:'/employee/page',fitColumns:true,singleSelect:true,fit:true,pagination:true,toolbar:'#tb'">
<thead>
<tr>
<th data-options="field:'id',width:100">编码</th>
<th data-options="field:'username',width:100">名称</th>
<th data-options="field:'password',width:100,align:'right'">密码</th>
<th data-options="field:'email',width:100,align:'right'">邮箱</th>
<th data-options="field:'age',width:100,align:'right'">年龄</th>
</tr>
</thead>
</table>
②分页查询后台代码
@Controller
@RequestMapping("/employee")
public class EmployeeController extends BaseController{
@Autowired
private IEmployeeService employeeService;
@RequestMapping("/index")
public String index(){
return "employee/index";
}
@RequestMapping("/page")
@ResponseBody
public Page page(EmployeeQuery query){
//返回Page对象
return employeeService.queryPageAndSort(query);
}
}
测试发现页面没有数据显示,但是前台获取到了数据
为什么呢?因为前台easyui要求{total:xx,rows:xxx}的json格式
- total:数据总条数
- rows:每页行数(每页条数)
所以我们后台准备了UIPage类返回符合前台要求的数据
③分页数据不显示问题(UIPage类)
创建common包,包中创建UIPage类
public class UIPage<T> {
private Long total;
private List<T> rows = new ArrayList<>();
public UIPage(Page page) {
this.total = page.getTotalElements();//总条数
this.rows = page.getContent();//每页行数(条数)
}
public Long getTotal() {
return total;
}
public void setTotal(Long total) {
this.total = total;
}
public List<T> getRows() {
return rows;
}
public void setRows(List<T> rows) {
this.rows = rows;
}
}
分页查询返回UIPage对象(json数据格式)
@RequestMapping("/page")
@ResponseBody
public UIPage page(EmployeeQuery query){
//返回UIPage对象
return new UIPage(employeeService.queryPageAndSort(query));
}
④分页翻页无反应问题
再次测试成功显示分页信息,但是发现切换页数数据不发生变化
因为前台数据与后台数据名称不匹配
page-----currentPage(当前页数)
rows-----pageSize(每页行数)
解决方法:为BaseQuery(查询对象父类添加setPage和setPageSize方法)
这样page和rows就可以成功封装到查询对象中
setPage方法
//兼容easyui
public void setPage(int page) {
this.currentPage = page;
}
setPageSize方法
//兼容easyui
public void setRows(int rows) {
this.pageSize = rows;
}
测试后,分页查询完成了
6.高级条件查询
①为index.jsp添加复杂工具栏
将工具栏与表格关连 toolbar:’#tb’
<div id="tb" style="padding:5px;height:auto">
<div style="margin-bottom:5px">
<a href="#" data-method="add" class="easyui-linkbutton" iconCls="icon-add" plain="true">添加</a>
<a href="#" data-method="update" class="easyui-linkbutton" iconCls="icon-edit" plain="true">修改</a>
<a href="#" data-method="delete" class="easyui-linkbutton" iconCls="icon-remove" plain="true">删除</a>
</div>
<form id="searchForm" method="post">
用户名: <input name="username" class="easyui-textbox" style="width:80px">
邮箱: <input name="email" class="easyui-textbox" style="width:80px">
<a href="#" data-method="search" class="easyui-linkbutton" iconCls="icon-search">查询</a>
</form>
</div>
<%--表格--%>
<table id="datagrid" class="easyui-datagrid" style="width:400px;height:250px"
data-options="url:'/employee/page',fitColumns:true,singleSelect:true,fit:true,pagination:true,toolbar:'#tb'">
<thead>
<tr>
<th data-options="field:'id',width:100">编码</th>
<th data-options="field:'username',width:100">名称</th>
<th data-options="field:'password',width:100,align:'right'">密码</th>
<th data-options="field:'email',width:100,align:'right'">邮箱</th>
<th data-options="field:'age',width:100,align:'right'">年龄</th>
</tr>
</thead>
</table>
②工具栏统一添加事件
为每个按钮添加统一绑定事件data-method="方法名"
$(function () {
//常用的元素,提前获取到
var datagrid = $("#datagrid");
var searchForm = $("#searchForm");
$("*[data-method]").on("click",function () {
//获取方法名 拿到当前调用方法的对象
var methodName = $(this).data("method");
//动态调用方法
itsource[methodName]();
})
itsource = {
add(){
alert("add");
},
update(){
alert("update");
},
delete(){
alert("delete");
},
search(){
//拿到查询的值 返回的json格式
var params = searchForm.serializeObject();
//高级条件查询
datagrid.datagrid("load",params);
}
}
})
③实现高级查询
serializeObject()方法需要引入jquery.jdirk.js文件,返回表单所以的属性值(json格式)
search(){
//拿到查询的值 返回的json格式
var params = searchForm.serializeObject();
//高级条件查询
datagrid.datagrid("load",params);
}
7.实现删除
①前台
前台思路:
- 判断是否选中一行
- 选中就弹出确认框
- 确认就执行删除操作,发送ajax请求,将id传给后台,并回调函数
- 若成功就重新加载页面
- 失败就提示失败信息
- 没选中,提示没选中,结束代码
前台代码:
- 选中就弹出确认框
delete(){
//获取选中的行
var row = datagrid.datagrid("getSelected");
//判断是否选中一行
if(!row){//如果这行不存在
//提示选中
$.messager.alert('提示','请选中一行再删除','warning');
return;
}
//选中弹出确认框
$.messager.confirm('确认','您确认想要删除记录吗?',function(r){
if (r){
//放松ajax请求 参数1:请求路径
$.get("/employee/delete",{id:row.id},function (result) {
if(result.success){
//删除成功,重新加载页面
datagrid.datagrid("reload");
}else{
//删除失败,提示失败原因
$.messager.alert('提示',`删除失败了,原因是:${result.msg}`,'error');
}
})
}
});
}
②后台
后台思路:
- 创建一个返回结果类JpaResult,删除操作后返回该对象
- controller层,创建delete方法,返回JpaResult对象的json格式数据
后台代码:
JPAResult类
public class JpaResult {
//默认成功为true
private boolean success = true;
//错误提示信息
private String msg;
//无参构造,默认成功不传错误信息
public JpaResult() {
}
//有参构造,表示失败
public JpaResult(boolean success, String msg) {
this.success = success;
this.msg = msg;
}
public boolean isSuccess() {
return success;
}
public void setSuccess(boolean success) {
this.success = success;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
controller层delete方法
/*
* 删除后会返回一个json数据{success:true/false,msg:xxx}
* */
@RequestMapping("/delete")
@ResponseBody
public JpaResult delete(Long id){
try {
employeeService.delete(id);
//成功
return new JpaResult();
} catch (Exception e) {
e.printStackTrace();
//失败
return new JpaResult(false,e.getMessage());
}
}