结构图
Pepository层
自定义一个Repository, 它是JpaRepository的功能基础上继承增强
自定义接口
如自定义接口 BaseRepository 继承JpaRepository,增加自己想要的功能
然后在接口BaseRepository上加@NoRepositoryBean注解
这样Spring Data Jpa在启动时就不会去实例化(去实现)BaseRepository这个接口,而由自己写BaseRepositoryImpl去实现
自定义接口代码:
package com.wwy.repository;
import com.wangweiyu.query.BaseQuery;
import org.springframework.data.domain.Page;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.repository.NoRepositoryBean;
import java.io.Serializable;
import java.util.List;
/**
* 自定义一个Repository, 它是JpaRepository的功能基础上继承增强
*
* 在上面添加@NoRepositoryBean标注
* 这样Spring Data Jpa在启动时就不会去实例化BaseRepository这个接口
*
*/
/*不能确定类型 所以写的泛型 <T,ID>*/
@NoRepositoryBean
public interface BaseRepository<T,ID extends Serializable> extends JpaRepository<T,ID>,JpaSpecificationExecutor<T> {
//根据Query拿到分页对象(分页)
Page findPageByQuery(BaseQuery baseQuery);
//根据Query拿到对应的所有数据(不分页)
List<T> findByQuery(BaseQuery baseQuery);
//根据jpql与对应的参数拿到数据
List findByJpql(String jpql,Object... values);
}
BaseRepositoryImpl功能实现
定义好自定义的方法后,我们现在通过一个基本的Repository类来实现该方法:
首先添加BaseRepositoryImpl类,继承SimpleJpaRepository类,使其拥有Jpa Repository的基本方法。
我们发现Repository有两个构造函数:
1 SimpleJpaRepository(JpaEntityInformation entityInformation, EntityManager entityManager)
2 SimpleJpaRepository(Class domainClass, EntityManager em)
这里我们实现第二个构造函数,拿到domainClass和EntityManager两个对象。
BaseRepositoryImpl代码
package com.wangweiyu.repository;
import com.wangweiyu.query.BaseQuery;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import java.io.Serializable;
import java.util.List;
public class BaseRepositoryImpl<T,ID extends Serializable> extends SimpleJpaRepository<T,ID> implements BaseRepository<T,ID> {
private final EntityManager entityManager;
//必需要实现父类的这个构造器
public BaseRepositoryImpl(Class<T> domainClass, EntityManager em) {
super(domainClass, em);
this.entityManager = em;
}
@Override
public Page findPageByQuery(BaseQuery baseQuery) {
//第一步:拿到所有高级查询条件
Specification spec = baseQuery.createSpec();
//第二步:拿到排序的值
Sort sort = baseQuery.createSort();
//第三步:根据条件查询分页数据并且返回
Pageable pageable = new PageRequest(baseQuery.getCurrentPage_2(), baseQuery.getPageSize(),sort);
Page<T> page = super.findAll(spec, pageable);
return page;
}
@Override
public List<T> findByQuery(BaseQuery baseQuery) {
//第一步:拿到所有高级查询条件
Specification spec = baseQuery.createSpec();
//第二步:拿到排序的值
Sort sort = baseQuery.createSort();
//第三步:拿到数据返回
return findAll(spec, sort);
}
@Override
public List findByJpql(String jpql, Object... values) {
//第一步:创建Query对象
Query query = entityManager.createQuery(jpql);
//第二步:把值设置到Query对象中去
if (values!=null) {
for (int i = 0; i < values.length; i++) {
query.setParameter(i + 1, values[i]);
}
}
//第三步:返回数据
return query.getResultList();
}
}
创建自定义创建自定义RepositoryFactoryBean
接下来创建一个自定义的RepositoryFactoryBean来代替默认的RepositoryFactoryBean。
RepositoryFactoryBean负责返回一个RepositoryFactory,Spring Data Jpa 将使用RepositoryFactory来创建Repository具体实现,这里我们***用BaseRepositoryImpl代替SimpleJpaRepository作为Repository接口的实现***。这样我们就能够达到为所有Repository添加自定义方法的目的。
我们需要覆写创建RepositoryFactory的方法:createRepositoryFactory
package com.wangweiyu.repository;
import org.springframework.data.jpa.repository.support.JpaRepositoryFactory;
import org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.core.RepositoryInformation;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
import javax.persistence.EntityManager;
import java.io.Serializable;
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);
}
//确定功能对象的类型
@Override
protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
return BaseRepositoryImpl.class;
}
}
}
然后配置applicationContext.xml
在xml配置文件加上 factory-class="com.wangweiyu.repository.BaseRepositoryFactoryBean"
这样就会自动去查找BaseRepositoryImpl实现层
<jpa:repositories base-package="com.wangweiyu.repository"
entity-manager-factory-ref="entityManagerFactory"
transaction-manager-ref="transactionManager"
factory-class="com.wangweiyu.repository.BaseRepositoryFactoryBean"
/>
进行测试:看是否加入了增强功能
1 测试 PageByQuerry功能
/*
*测试BaseRepository 增加的功能
* */
@Test
public void test_Find() throws Exception{
EmployeeQuery query = new EmployeeQuery();
query.setUsername("1");
Page<Employee> pageByQuery = employeeRepository.findPageByQuery(query);
pageByQuery.forEach(employee -> System.out.println(employee));
}
Query对象在之后的学习中会前台传过来,会简化此处代码
2 测试 findByQuery功能
同上,自行测试
3 测试 findByJpql功能
同上,自行测试
Service层
创建BaseService接口层——作为公共的Service抽取
BaseService代码
import org.springframework.data.domain.Page;
import java.io.Serializable;
import java.util.List;
public interface IBaseService<T,ID> extends Serializable {
//添加与修改数据
void save(T t);
void delete(ID id);
//查询数据
T findOne(ID id);
List<T> findAll();
//根据Query拿到分页对象(分页)
Page findPageByQuery(BaseQuery baseQuery);
//根据Query拿到对应的所有数据(不分页)
List<T> findByQuery(BaseQuery baseQuery);
//根据jpql与对应的参数拿到数据
List findByJpql(String jpql,Object... values);
}
BaseServiceImpl实现层代码
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.transaction.annotation.Transactional;
import java.io.Serializable;
import java.util.List;
import static org.springframework.transaction.annotation.Propagation.SUPPORTS;
@Transactional(readOnly = true,propagation = SUPPORTS)
public abstract class BaseServiceImpl<T,ID extends Serializable> implements IBaseService<T,ID> {
/**
* BaseRepository接口会有很多实现
* EmployeeRepository<Employee,Long>,DepartmentRepository<Department,Long>,RoleRepository<Role,Long>
* EmployeeService extends BaseServiceImpl<Employee,Long>
*/
//注意 注入要写泛型
//:Spring 4.x 中可以为子类注入子类对应的泛型类型的成员变量的引用
@Autowired
private BaseRepository<T,ID> baseRepository;
@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 Page findPageByQuery(BaseQuery baseQuery) {
return baseRepository.findPageByQuery(baseQuery);
}
@Override
public List<T> findByQuery(BaseQuery baseQuery) {
return baseRepository.findByQuery(baseQuery);
}
@Override
public List findByJpql(String jpql, Object... values) {
return baseRepository.findByJpql(jpql, values);
}
}
IEmployeeService层——继承IBaseService接口
IEmployeeService
//这里做最简单的CRUD,在开发中,业务后期是有很多功能的。此处层需要以后再添加功能
public interface IEmployeeService extends IBaseService<Employee,Long> {
//如,在第二天将添加CheckName功能: Boolean checkUsername(String username);
}
EmployeeServiceImpl实现层代码
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class EmployeeServiceImpl extends BaseServiceImpl<Employee,Long> implements IEmployeeService {
// @Override
// public Boolean checkUsername(String username) {
// return null;
// }
@Autowired
private EmployeeRepository employeeRepository;
/**
* 判断返回得到有相同名字得数量,大于等于1则不能改咯
*/
@Override
public Boolean checkUsername(String username) {
return !(employeeRepository.getCountByUsername(username)>0);
}
}
集成SpringMVC
引入相应jar包
<!-- 引入web前端的支持 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${org.springframework.version}</version>
</dependency>
配置applicationContext-mvc.xml
为SpringMVC单独配置一个xml,把它与核心的applicationContext.xml分开。使xml配置也是模块化配置的。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
">
<!-- 对静态资源进行放行 -->
<mvc:default-servlet-handler />
<!-- 扫描controller部分的包 -->
<!-- @Component组件, @Repository持久层, @Service业务逻辑层, and @Controller控制器 -->
<context:component-scan base-package="com.wangweiyu.web" />
<!-- 添加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="com.wangweiyu.common.CustomMapper"></bean>
</property>
</bean>
</mvc:message-converters>
</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>
<!-- 错误:提示告诉开发者你没有配置文件上传解析器。 必须叫 multipartResolver 不能改 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 设置上传文件的最大尺寸为1MB 即 1048576KB-->
<property name="maxUploadSize">
<value>1048576</value>
</property>
</bean>
</beans>
配置web.xml
<?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">
<!-- 读取Spring的配置文件 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!-- 启动Spring的监听器 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--控制控制器-->
<!-- 配置核心控制器-->
<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>
<!--配置过滤器
目的:让EntityManager对象在我的jso页面显示完毕之后再关闭
-->
<filter>
<filter-name>openEM</filter-name>
<filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>openEM</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 配置解决中文乱码的问题 -->
<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>
</web-app>
创建Controller与页面
EmployeeController
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping("/employee")
public class EmoloyeeController extends BaseController{
@Autowired
private IEmployeeService employeeService;
@RequestMapping("/index")
public String index(){
return "employee/employee";
}
@RequestMapping("/page")
@ResponseBody
public UiPage<Employee> page(EmployeeQuery querry){
Page page = employeeService.findPageByQuery(querry);
return new UiPage(page);
}
/**
*ModelAttribute:路径访问Controller的每个方法,都会先执行它里面的代码
*/
@ModelAttribute("editEmployee")
public Employee beforeEdit(Long id,String cmd){
if(id!=null && "_update".equals(cmd)){
//修改才执行这个代码
Employee dbEmployee = employeeService.findOne(id);
//解决n-to-n的问题,把关联对象设置为null
dbEmployee.setDepartment(null);
return dbEmployee;
}
return null;
}
//添加
@RequestMapping("/save")
@ResponseBody
public JsonResult save(Employee employee){
try {
employeeService.save(employee);
return new JsonResult();
} catch (Exception e) {
e.printStackTrace();
return new JsonResult(false,e.getMessage());
}
}
//修改
@RequestMapping("/update")7
@ResponseBody
public JsonResult update(@ModelAttribute("editEmployee")Employee employee){
try {
employeeService.save(employee);
return new JsonResult();
} catch (Exception e) {
e.printStackTrace();
return new JsonResult(false,e.getMessage());
}
}
/**
* 删除功能,前台要求返回{success:true/false,msg:xxx}
* @return
*/
@RequestMapping("/delete")
@ResponseBody
public JsonResult delete(Long id){
try {
employeeService.delete(id);
return new JsonResult();
} catch (Exception e) {
e.printStackTrace();
return new JsonResult(false,e.getMessage());
}
}
@RequestMapping("/checkUsername")
@ResponseBody
public Boolean checkUsername(Long id,String username){
//1.判断id是否存在
if(id!=null){
//2.根据id查询对象
Employee dbEmp = employeeService.findOne(id);
//3.判断名称是否相等
if(username.equals(dbEmp.getUsername())){
//4.如果相等,直接返回true
return true;
}
}
return employeeService.checkUsername(username);
}
}