一,什么是SpringDataJpa
咱们要集成三大框架(Spring+SpringMVC+SpringDataJpa)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.itsource</groupId>
<artifactId>yxb</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<org.springframework.version>4.2.5.RELEASE</org.springframework.version>
<org.hibernate.version>4.3.8.Final</org.hibernate.version>
<spring-data-jpa.version>1.9.0.RELEASE</spring-data-jpa.version>
<com.fasterxml.jackson.version>2.5.0</com.fasterxml.jackson.version>
<org.slf4j.version>1.6.1</org.slf4j.version>
</properties>
<dependencies>
<!-- Spring的支持包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${org.springframework.version}</version>
<scope>test</scope>
</dependency>
<!-- 引入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>
<!-- SpringMCV上传需要用到io包-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-io</artifactId>
<version>1.3.2</version>
</dependency>
<!-- 文件上传用到的包 -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.2.2</version>
</dependency>
<!-- SpringMVC的json支持包 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${com.fasterxml.jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${com.fasterxml.jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${com.fasterxml.jackson.version}</version>
</dependency>
<!-- hibernate的支持包 -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${org.hibernate.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${org.hibernate.version}</version>
</dependency>
<!-- SpringData的支持包 -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>${spring-data-jpa.version}</version>
</dependency>
<!-- SpringData的擴展包 -->
<dependency>
<groupId>com.github.wenhao</groupId>
<artifactId>jpa-spec</artifactId>
<version>3.1.1</version>
<!-- 把所有的依賴都去掉 -->
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.5</version>
</dependency>
<!-- 測試包 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<!-- 这个scope 只能作用在编译和测试时,同时没有传递性。表示在运行的时候不添加此jar文件 -->
<scope>provided</scope>
</dependency>
<!-- 日志文件 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${org.slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${org.slf4j.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.14</version>
</dependency>
<!-- 代码生成器模版技术 -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity</artifactId>
<version>1.6</version>
</dependency>
<!-- shiro的支持包 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-all</artifactId>
<version>1.4.0</version>
<type>pom</type>
</dependency>
<!-- shiro与Spring的集成包 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
<!-- poi支持的jar包 -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.11</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.11</version>
</dependency>
<!-- 图片压缩功能 -->
<!-- 缩略图 -->
<dependency>
<groupId>net.coobird</groupId>
<artifactId>thumbnailator</artifactId>
<version>0.4.6</version>
</dependency>
<!-- 定时调度 -->
<dependency>
<groupId>quartz</groupId>
<artifactId>quartz</artifactId>
<version>1.5.2</version>
</dependency>
<!-- 邮件支持 -->
<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
<version>1.4.1</version>
</dependency>
</dependencies>
<build>
<finalName>yxb</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>8.1.15.v20140411</version>
<configuration>
<stopPort>9966</stopPort>
<stopKey>foo</stopKey>
<webAppConfig>
<contextPath>/</contextPath>
</webAppConfig>
</configuration>
</plugin>
</plugins>
</build>
</project>
二,SpringDataJpa的基本使用(掌握)
抽取id
@MappedSuperclass
public class BaseDomain {
@Id
@GeneratedValue
public Long id;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
抽取查询方法
BaseQuery公共查询对象,完成分页查询和排序
public abstract class BaseQuery {
//当前页数
private Integer currentPage=1;
//每页条数
private Integer pageSize=10;
//排序字段
private String orderByName;
//排序类型默认 降序
private String orderByType="DESC";
public Integer getJpaPage(){
return currentPage -1;
}
public Integer getCurrentPage() {
return currentPage;
}
public void setCurrentPage(Integer currentPage) {
this.currentPage = currentPage;
}
public Integer getPageSize() {
return pageSize;
}
public void setPageSize(Integer pageSize) {
this.pageSize = pageSize;
}
public String getOrderByName() {
return orderByName;
}
public void setOrderByName(String orderByName) {
this.orderByName = orderByName;
}
public String getOrderByType() {
return orderByType;
}
public void setOrderByType(String orderByType) {
this.orderByType = orderByType;
}
//排序方法,设置需要排序才排序,不需要就不排序
public Sort createSort(){
Sort sort = null;
if (orderByName!=null){
//三目运算
Sort.Direction type ="ASC".equals(orderByType.toUpperCase())?Sort.Direction.ASC:Sort.Direction.DESC;
//组装sort
sort = new Sort(type,orderByName);
}
return sort;
}
//该方法定义成抽象的目的,让子类都必须覆写该方法
public abstract Specification createSpecification();
}
三,EmployeeRepository extends JpaRepository
//想通过员工的名称来查询员工 -- 如果按照一定的规范来写 底层就会解析完成
List<Employee> findEmployeeByUsername(String username);
//like模糊查询
List<Employee> findEmployeeByUsernameLike(String username);
//查询数据--Query注解查询
@Query("select o from Employee o where o.username like ?1")
List<Employee> findEmpByUsername(String username);
//根据顺序查询
@Query("select o from Employee o where o.username like ?1 and o.email like ?2")
List<Employee> query02(String username,String emial);
//根据名称来查询
@Query("select o from Employee o where o.username like :username and o.email like :email")
List<Employee> query03(@Param("username") String username1, @Param("email")String email1);
//对原生sql语句
@Query(value="select count(*) from employee ",nativeQuery=true)
Long query04();
还可以继承多个接口
public interface EmployeeRepository1 extends JpaRepository<Employee,Long>,
JpaSpecificationExecutor<Employee> {
}
四,各种CRUD ,分页查询,高级搜索,
CRUD
@Autowired
private EmployeeRepository employeeRepository;
//查询所有
@Test
public void testFindAll() throws Exception{
List<Employee> employees = employeeRepository.findAll();
for (Employee employee : employees) {
System.out.println(employee);
}
}
//批量查询
@Test
public void testFindAll1() throws Exception{
List list = Arrays.asList(55L,66L,77L);
List<Employee> employees = employeeRepository.findAll(list);
for (Employee employee : employees) {
System.out.println(employee);
}
}
@Autowired
private EmployeeRepository employeeRepository;
//查询所有
@Test
public void testJpa() throws Exception{
List<Employee> lists = employeeRepository.findAll();
System.out.println(employeeRepository.getClass());
for (Employee employee :lists ) {
System.out.println(employee);
}
}
@Test
public void testfindAll() throws Exception{
//表示多条查询查询25,26,27
List lists = Arrays.asList(25L,26L,27L);
List<Employee> list = employeeRepository.findAll(lists);
for (Employee o : list) {
System.out.println(o);
}
}
//查询一条数据
@Test
public void testFindOne() throws Exception{
Employee employee = employeeRepository.findOne(6L);
System.out.println(employee);
}
//保存
@Test
public void testsave() throws Exception{
Employee employee = new Employee();
employee.setUsername("公鸡");
employee.setPassword("傻逼");
employee.setAge(22);
employeeRepository.save(employee);
}
//修改 如果有id 就修改 没有id 就新增
@Test
public void testUpdate() throws Exception{
Employee employee = new Employee();
employee.setId(275L);
employee.setUsername("大鸡鸡");
employee.setPassword("大傻逼");
employee.setAge(18);
employeeRepository.save(employee);
}
//删除
@Test
public void testDelete() throws Exception{
employeeRepository.delete(275L);
}
@Test
public void testDeleteInBatch() throws Exception{
//指定删除两条
List<Employee> list = employeeRepository.findAll(Arrays.asList(272L, 273L));
employeeRepository.deleteInBatch(list);
}
//总条数
@Test
public void testCount() throws Exception{
System.out.println(employeeRepository.count());
}
JpaSpecificationExecutor
@Autowired
private EmployeeRepository1 employeeRepository1;
@Test
public void testSpec1() throws Exception{
EmployeeQuery employeeQuery = new EmployeeQuery();
employeeQuery.setUsername("呵呵");
employeeQuery.setAge(234);
Specification<Employee> spe = Specifications.<Employee>and().
like(StringUtils.isNotBlank(employeeQuery.getUsername()),
"username", "%" + employeeQuery.getUsername() + "%")
.gt(employeeQuery.getAge() != null, "age", employeeQuery.getAge())
.build();
//排序
Sort sort = null;
if(employeeQuery.getOrderByName()!= null){
String orderByName = employeeQuery.getOrderByName();
Sort.Direction direction = null;
if(employeeQuery.getOrderByType().equals("DESC")){
direction = Sort.Direction.DESC;
}else{
direction = Sort.Direction.ASC;
}
//组装Sort
sort =new Sort(direction,orderByName);
}
//分页
Pageable pageable = new PageRequest(employeeQuery.getJpaPage(), employeeQuery.getPageSize(), sort);
Page<Employee> employees = employeeRepository1.findAll(spe, pageable);
for (Employee employee : employees) {
System.out.println(employee);
}
}
@Test
public void testSpec2() throws Exception{
EmployeeQuery employeeQuery = new EmployeeQuery();
employeeQuery.setUsername("1");
employeeQuery.setAge(18);
Specification spec = employeeQuery.createSpecification();
Sort sort = employeeQuery.createSort();
Pageable pageable = new PageRequest(employeeQuery.getJpaPage(), employeeQuery.getPageSize(), sort);
Page<Employee> lists = employeeRepository1.findAll(spec, pageable);
for (Employee employee : lists) {
System.out.println(employee);
}
}
Query的抽取
@Autowired
private EmployeeRepository employeeRepository;
//按照规范查询
@Test
public void testQuery() throws Exception{
List<Employee> employees = employeeRepository.findEmployeeByUsername("acac");
for (Employee employee : employees) {
System.out.println(employee);
}
}
//按照规范查询
@Test
public void testQuery1() throws Exception{
//模糊查询
List<Employee> employees = employeeRepository.findEmployeeByUsernameLike("%bb%");
for (Employee employee : employees) {
System.out.println(employee);
}
}
//Query注解查询
@Test
public void testQuery2() throws Exception{
List<Employee> emps= employeeRepository.findEmpByUsername("%bbbbbb%");
for (Employee emp : emps) {
System.out.println(emp);
}
}
//根据顺序来查询
@Test
public void testQuery3() throws Exception{
//多条件查询
List<Employee> emps= employeeRepository.query02("%cc%","%33%");
for (Employee emp : emps) {
System.out.println(emp);
}
}
@Test
public void testQuery4() throws Exception{
List<Employee> emps= employeeRepository.query03("b","%2%");
for (Employee emp : emps) {
System.out.println(emp);
}
}
@Test
public void testQuery5() throws Exception{
System.out.println(employeeRepository.query04());
}
springdatajpa的扩展–抽取
抽取一个BaseRepository – 写三个方法
@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
public class BaseRepositoryImpl<T,ID extends Serializable> extends SimpleJpaRepository<T,ID>
implements BaseRepository<T,ID> {
//让spring把entityManager给我注入过来 --factoryBean
private final EntityManager entityManager;
//必需要实现父类的这个构造器
public BaseRepositoryImpl(Class<T> domainClass, EntityManager em) {
super(domainClass, em);
this.entityManager = em;
}
//分页查询的方法
@Override
public Page findPageByQuery(BaseQuery baseQuery) {
//得到Sort
Sort sort = baseQuery.createSort();
//条件
Specification spec = baseQuery.createSpecification();
//分页
Pageable pageable = new PageRequest(baseQuery.getJpaPage(),baseQuery.getPageSize() , sort);
//查询
Page<T> page = super.findAll(spec,pageable);
return page;
}
//不分页
@Override
public List<T> findByQuery(BaseQuery baseQuery) {
Sort sort = baseQuery.createSort();
Specification specs = baseQuery.createSpecification();
//条件和排序
List<T> list = super.findAll(specs,sort);
return list;
}
//select o from Employee o where o.username=? and o.email=?
//xxx 111@email.com 2
@Override
public List findByJpql(String jpql, Object... values) {
//得到query对象
Query query = entityManager.createQuery(jpql);
//设值
for (int i = 0; i < values.length; i++) {
query.setParameter(i+1,values[i] );
}
return query.getResultList();
}
然后管理起来自己用
二,创建web前端结构
完成Service层
创建IBaseService
public interface IBaseService<T,ID extends Serializable> {
//新增和修改
void save(T t);
//删除 --Long
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
@Transactional(propagation = Propagation.SUPPORTS,readOnly = true)
public class BaseServiceImpl<T,ID extends Serializable> implements IBaseService<T,ID> {
//spring 4之后 支持泛型的注解
@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
public interface IEmployeeService extends IBaseService<Employee,Long> {
}
创建EmployeeServiceImpl
@Service
public class EmployeeServiceImpl extends BaseServiceImpl<Employee,Long>
implements IEmployeeService {
}
配置applicationContext-mvc.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="cn.itsource.aisell.web.controller" />
<!-- 添加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:配置 setObjectMapper(new CustomMapper)objectMapper 为我们自定义扩展后的 CustomMapper,解决了返回对象有关系对象的报错问题 -->
<property name="objectMapper">
<bean class="cn.itsource.aisell.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>
<!-- 错误:提示告诉开发者你没有配置文件上传解析器。 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 设置上传文件的最大尺寸为1MB -->
<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">
</web-app>
完成Controller层
@Controller
@RequestMapping("/employee")
public class EmployeeController {
@Autowired
private IEmployeeService employeeService;
@RequestMapping("/index")
public String index() {
//根据配置,这里会跳到/WEB-INF/views/employee/employee.jsp页面
return "employee/employee";
}
@RequestMapping("/list")
@ResponseBody
public List<Employee> list(){
return employeeService.findAll();
}
}
三,完成员工页面前端展示和分页
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@include file="/WEB-INF/views/head.jsp"%>
<html>
<head>
<title>Title</title>
<script type="text/javascript" src="/js/model/employee.js"></script>
</head>
<body>
<!--
datagrid fit 填充父容器
rownumbers 行号
pagination 分页
-->
<table id="employeeGrid" class="easyui-datagrid" data-options="fit:false,fixed:true,
fitColumns:true,toolbar:'#tb',singleSelect:true";
url="/employee/page"
iconCls="icon-save"
rownumbers="true" pagination="true">
<thead>
<tr>
<th width="20" field="headImage" align="right" formatter="formatImage">头像</th>
<th width="20" field="username" >用户名</th>
<th width="20" field="password">密码</th>
<th width="20" field="email">邮件</th>
<th width="20" field="age" align="right">年龄</th>
<th width="20" field="department" align="right" formatter="formatObj">部门名称</th>
</tr>
</thead>
</table>
<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="edit" class="easyui-linkbutton" iconCls="icon-edit" plain="true">修改</a>
<a href="#" data-method="del" class="easyui-linkbutton" iconCls="icon-remove" plain="true">删除</a>
</div>
<!-- 这部门是查询的功能 -->
<div>
<form id="searchForm" action="/employee/download" method="post">
用户名: <input name="username" class="easyui-textbox" style="width:80px;height:32px">
邮件: <input name="email" class="easyui-textbox" style="width:80px;height:32px">
部门 :
<input class="easyui-combobox" name="departmentId"
data-options="valueField:'id',textField:'name',panelHeight:'auto',url:'/util/departmentList'">
<a href="#" data-method="search" class="easyui-linkbutton" iconCls="icon-search">查找</a>
</form>
</div>
</div>
</body>
</html>
解决懒加载问题
<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>
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);
}
}
加入高级查询条件
UtilController
@Controller
@RequestMapping("/util")
public class UtilController {
//注入部门信息
@Autowired
private IDepartmentService departmentService;
//departmentList
@RequestMapping("/departmentList")
@ResponseBody
public List<Department> queryDepartmentList(){
return departmentService.findAll();
}
}
$(function(){
//定义form表单
var searchForm = $("#searchForm");
var employeeGrid = $("#employeeGrid");
//绑定事件 easyui 第二天的时候
$("a[data-method]").on('click',function(){
//获取 data-method属性 <a data-method="seacher">
var methodName = $(this).data("method");
//动态调用方法 itsource["seacher"]
itsource[methodName]();
})
//对象
var itsource = {
search:function(){
//怎么完成高级查询 jquery.jdirk.js 这个方法 这是jquery扩展方法
//该方法返回一个 JSON Object,返回对象中的每个数据都表示一个表单控件值。
var param = searchForm.serializeObject();
//发送查询数据库 --加载表格 发送请求 /employee/page
employeeGrid.datagrid('load',param);
},
add:function(){
alert(1);
}
}
public class EmployeeQuery extends BaseQuery{
/**
* username admin
* email xx@qq.com
* departmentId 1
*/
//自身条件
private String username;
private String email;
private Integer age;
private Integer departmentId;
public Integer getDepartmentId() {
return departmentId;
}
public void setDepartmentId(Integer departmentId) {
this.departmentId = departmentId;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
//抽取查询
@Override
public Specification createSpecification() {
Specification<Employee> spe = Specifications.<Employee>and().
like(StringUtils.isNotBlank(username), "username", "%" + username + "%")
.like(StringUtils.isNotBlank(email), "email", "%" + email + "%")
.eq(departmentId!=null,"department.id",departmentId)
.gt(age != null, "age", age)
.build();
return spe;
}
}
一,智能销售CRUD
后台
@Controller
@RequestMapping("/employee")
public class EmployeeController {
@Autowired
private IEmployeeService employeeService;
//加载页面
@RequestMapping("/index")
public String index(){
return "employee";
}
//分页处理
@RequestMapping("/page")
@ResponseBody
public UiPage page(EmployeeQuery employeeQuery){
Page page = employeeService.findPageByQuery(employeeQuery);
UiPage uipage = new UiPage(page);
return uipage;
}
//方法 delete
@RequestMapping("/delete")
@ResponseBody
public Map delete(Long id){
Map mp = new HashMap();
try {
employeeService.delete(id);
mp.put("success", true);
} catch (Exception e) {
e.printStackTrace();
mp.put("success", false);
mp.put("msg", e.getMessage());
}
return mp;
}
//保存方法-- 修改和新增保存
@RequestMapping("/save")
@ResponseBody
public Map save(Employee employee){
Map mp = new HashMap();
try {
employeeService.save(employee);
mp.put("success", true);
} catch (Exception e) {
e.printStackTrace();
mp.put("success", false);
mp.put("msg", e.getMessage());
}
return mp;
}
@RequestMapping("/update")
@ResponseBody
public Map update(@ModelAttribute("editEmployee") Employee employee){
Map mp = new HashMap();
try {
employeeService.save(employee);
mp.put("success", true);
} catch (Exception e) {
e.printStackTrace();
mp.put("success", false);
mp.put("msg", e.getMessage());
}
return mp;
}
//去重
@RequestMapping("/checkUsername")
@ResponseBody
public boolean checkUsername(String username,Long id){
if(id != null && !"".equals(id)){
//修改
Employee employee = employeeService.findOne(id);
String username_db = employee.getUsername();
if(username.equals(username_db)){
return true;
}
}
return employeeService.checkUsername(username);
}
//删除方法
@ModelAttribute("editEmployee")
public Employee beforeEdit(Long id,String cmd){
Employee employee = null;
if("update".equals(cmd) && id != null && !"".equals(id) ){
employee = employeeService.findOne(id);
employee.setDepartment(null);
}
return employee;
}
}
前台处理
/表格数据格式化
function formatObj(value){
if(value){
return value.name;
}
}
//formatImage头像格式化
function formatImage(value){
if(value){
return "<img width='100px' height='100px' src='"+value+"' alt='没有图片'/>"
}
}
//自己去扩展easyui验证的功能
$.extend($.fn.validatebox.defaults.rules, {
equals: {
validator: function(value,param){
return value == $(param[0]).val();
},
message: '密码不匹配.'
}
});
//扩展用户名是否重复的功能
$.extend($.fn.validatebox.defaults.rules, {
checkUsername: {
validator: function(value,param){
//当然输入框value 和数据库的username进行比较
//发送ajax请求 --异步(ajax同步)
var id = $("#employeeId").val();
var result = $.ajax({
type: "POST",
url: "/employee/checkUsername",
data: "username="+value+"&id="+id,
async: false //同步
}).responseText;
return result == 'true';
},
message: '用户名重复了.'
}
});
$(function(){
//定义form表单
var searchForm = $("#searchForm");
var employeeGrid = $("#employeeGrid");
//获取employeeDialog
var employeeDialog = $("#employeeDialog");
//定义表单新增的form
var employeeForm = $("#employeeForm");
//绑定事件 easyui 第二天的时候
$("a[data-method]").on('click',function(){
//获取 data-method属性 <a data-method="seacher">
var methodName = $(this).data("method");
itsource[methodName]();
});
//对象
var itsource = {
search:function(){
var param = searchForm.serializeObject();
employeeGrid.datagrid('load',param);
},
add:function(){
//隐藏数据 display:none
$("tr[data-save]").show();
//取消密码验证
$("*[data-save] input").validatebox("enableValidation");
//清空表单
employeeForm.form('clear');
//新增 --弹出一个对话框--装一个表单
employeeDialog.dialog('center').dialog('open');
},
edit:function(){
//选择一条数据进行修改
var row = employeeGrid.datagrid('getSelected');
if(row){
//隐藏数据 display:none
$("tr[data-save]").hide();
//取消密码验证
$("*[data-save] input").validatebox("disableValidation");
//部门回显
if(row.department){
row["department.id"] = row.department.id;
}
//修改 -- 回显示数据
employeeForm.form('load',row);
}else{
//提示用户
$.messager.alert('温馨提示:','请修改其中一条','info');
return;
}
//弹出对话框
employeeDialog.dialog('center').dialog('open');
},
del:function(){
//判断表格里面是否选中得数据
var row = employeeGrid.datagrid('getSelected');
if(row){
//是否确认要删除数据
$.messager.confirm('温馨提示','是否要删除?',function(value){
if(value){
//获取id值
//发送ajax到后台进行删除数据
$.get('/employee/delete',{"id":row.id},function(data){
//返回json对象
if(data.success){
$.messager.alert('温馨提示:','删除成功','info');
//重新加载数据
employeeGrid.datagrid('reload');
}else{
$.messager.alert('温馨提示:','删除失败'+data.msg,'error');
}
});
}
})
}else{
//提示用户
$.messager.alert('温馨提示:','选中一条进行删除','info');
return;
}
},
save:function(){
var url = "/employee/save";
//获取隐藏域里面id值
var id = $("#employeeId").val();
if(id){
url = "/employee/update?cmd=update"
}
//保存方法 --提交表单的数据到后台
employeeForm.form('submit', {
url:url,
onSubmit: function(){
// 提交之前的验证
return employeeForm.form('validate');
},
success:function(data){
//字符串 转换成json对象
var dataJson = $.parseJSON(data);
if(dataJson.success){
$.messager.alert('温馨提示:','操作成功','info');
//重新加载数据
employeeGrid.datagrid('reload');
//关闭对话框
employeeDialog.dialog('close');
}else{
$.messager.alert('温馨提示:','保存失败'+dataJson.msg,'error');
employeeDialog.dialog('close');
}
}
});
}
}
})
二.模板技术(freemarker-velocity)
模板技术怎样输出数据
数据+模板(html文件,vm文件,ftl文件)=输出文本
Velocity模板技术可以实现的功能
动态页面静态化:xxx.html --动态页面页面 静态化
pom.xml:添加jar文件
<!-- 代码生成器模版技术 -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity</artifactId>
<version>1.6</version>
</dependency>
测试Velocity功能
public class VelocityTest {
@Test
public void testVelocity01() throws Exception {
//创建模板应用上下文
VelocityContext context = new VelocityContext();
context.put("msg", "花好月圆");
//拿到相应的模板(需要设置好编码)
Template template = Velocity.getTemplate("temptest/hello.html","UTF-8");
//准备输出流
StringWriter writer = new StringWriter();
template.merge(context, writer);
System.out.println(writer);
}
@Test
public void testVelocity02() throws Exception {
//创建模板应用上下文
VelocityContext context = new VelocityContext();
context.put("msg", "男上加男");
//拿到相应的模板(需要设置好编码)
Template template = Velocity.getTemplate("temptest/hello.html","UTF-8");
//准备输出流
File file = new File("temptest/helloNew.html");
FileWriter writer = new FileWriter(file);
template.merge(context, writer);
writer.close();
}
}
三,代码生成器(手动写代码生成代码生成器)
一.开发步骤
1.准备模板(把每个模块需要改的地方确定好)
2.确定所有模板的生成顺序
3.确定所有模板的生成位置
4.确定要生成的基本功能的domain(Dept)
5.根据Domain名称与模板结合,在相应位置生成文件
6.解决如果父文件夹不存在的问题
7.解决如果文件已经存在的问题
8.排错(有错先改模板)
按照课件走,
@Test
public void testCreate() throws Exception{
//创建模板应用上下文
VelocityContext context = new VelocityContext();
//一.遍历所有的Domain
for (int i = 0; i < domains.length; i++) {
//1.1拿到大写的domain
String domainBig = domains[i];//Dept
//1.2拿到小写的domain //dept
String domainSmall = domainBig.substring(0,1).toLowerCase() + domainBig.substring(1);
//System.out.println(domainBig);
//System.out.println(domainSmall);
//1.3设置上下文的替换名称
context.put("Domain",domainBig);
context.put("domain",domainSmall);
//二.遍历所有的路径
for (int j = 0; j < paths.length; j++) {
//2.1拿到相应的路径
String path =paths[j];
//2.2拿到相应的模板名称
String tempName = tempNames[j];
//2.3拼接回我们需要的位置文件
String realPath = (path + tempName).replaceAll("Domain",domainBig).replaceAll("domain",domainSmall);
//三.准备相应文件与模板进行组合
//3.1准备相应的文件(要生成的文件)
File file = new File(realPath);
// 如果父文件不存在,我们创建一个
File parentFile = file.getParentFile();
if(!parentFile.exists()){
parentFile.mkdirs();
}
//3.2拿到相应的模板(需要设置好编码)
Template template = Velocity.getTemplate("template/"+tempName,"UTF-8");
FileWriter writer = new FileWriter(file);
template.merge(context, writer);
writer.close();
}
}
}
easycode
支持的数据库类型
因为是基于Database Tool开发,所有Database Tool支持的数据库都是支持的。
包括如下数据库:
1.MySQL
2.SQL Server
3.Oracle
4.PostgreSQL
5.Sqlite
6.Sybase
7.Derby
8.DB2
9.HSQLDB
10.H2
安装按照课件和老师讲的走
代码生成器模板
domain
##引入宏定义
$!define
##拿到首字母小写的表单
#set($lowerTableInfo = $tool.firstLowerCase($!{tableInfo.name}))
##使用宏定义设置回调(保存位置与文件后缀)
##注意:为了路径,我现在都从main的位置开始
#save("/main/java/cn/itsource/zx/domain", ".java")
##使用宏定义设置包后缀
#setPackageSuffix("domain")
##使用全局变量实现默认包导入
$!autoImport
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.Table;
##使用宏定义实现类注释信息
#tableComment("实体类")
@Entity
@Table(name="$!{lowerTableInfo}")
public class $!{tableInfo.name} extends BaseDomain {
##实现列进行排除
#set($temp = $tool.newHashSet("id"))
#foreach($item in $temp)
#set($newList = $tool.newArrayList())
#foreach($column in $tableInfo.fullColumn)
#if($column.name!=$item)
##带有反回值的方法调用时使用$tool.call来消除返回值
$tool.call($newList.add($column))
#end
#end
##重新保存
$tableInfo.setFullColumn($newList)
#end
#foreach($column in $tableInfo.fullColumn)
#if(${column.comment})//${column.comment}#end
private $!{tool.getClsNameByFullName($column.type)} $!{column.name};
#end
#foreach($column in $tableInfo.fullColumn)
##使用宏定义实现get,set方法
#getSetMethod($column)
#end
}
Query查询
##定义初始变量
#set($tableName =
t
o
o
l
.
a
p
p
e
n
d
(
tool.append(
tool.append(tableInfo.name, “Query”))
##设置文件名
!
c
a
l
l
b
a
c
k
.
s
e
t
F
i
l
e
N
a
m
e
(
!callback.setFileName(
!callback.setFileName(tool.append($tableName, “.java”))
##设置文件保存的位置(依然是从src开始)
!
c
a
l
l
b
a
c
k
.
s
e
t
S
a
v
e
P
a
t
h
(
!callback.setSavePath(
!callback.setSavePath(tool.append($tableInfo.savePath, “/main/java/cn/itsource/zx/query”))
##拿到首字母小写的表单
#set($lowerTableInfo =
t
o
o
l
.
f
i
r
s
t
L
o
w
e
r
C
a
s
e
(
tool.firstLowerCase(
tool.firstLowerCase(!{tableInfo.name}))
##引入相应的包
#if($tableInfo.savePackageName)package $!{tableInfo.savePackageName}.#{end}query;
##使用全局变量实现默认包导入
!
a
u
t
o
I
m
p
o
r
t
i
m
p
o
r
t
c
n
.
i
t
s
o
u
r
c
e
.
z
x
.
d
o
m
a
i
n
.
!autoImport import cn.itsource.zx.domain.
!autoImportimportcn.itsource.zx.domain.!{tableInfo.name};
import com.github.wenhao.jpa.Specifications;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.jpa.domain.Specification;
public class KaTeX parse error: Expected '}', got '#' at position 45: …s BaseQuery { #̲#注:如果生成的表没有name…!{tableInfo.name}>and()
.like(StringUtils.isNotBlank(name),“name”, “%”+name+"%")
.build();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Repository数据层
##定义初始变量
#set($tableName = $tool.append($tableInfo.name, "Repository"))
##设置回调
$!callback.setFileName($tool.append($tableName, ".java"))
$!callback.setSavePath($tool.append($tableInfo.savePath, "/main/java/cn/itsource/zx/repository"))
##拿到首字母小写的表单
#set($lowerTableInfo = $tool.firstLowerCase($!{tableInfo.name}))
##引入相应的包
#if($tableInfo.savePackageName)package $!{tableInfo.savePackageName}.#{end}repository;
import cn.itsource.zx.domain.$!{tableInfo.name};
public interface $!{tableInfo.name}Repository extends BaseRepository<$!{tableInfo.name},Long>{
}
IService业务层接口
##定义初始变量
#set($tableName = $tool.append("I",$tableInfo.name, "Service"))
##设置回调
$!callback.setFileName($tool.append($tableName, ".java"))
$!callback.setSavePath($tool.append($tableInfo.savePath, "/main/java/cn/itsource/zx/service"))
##拿到首字母小写的表单
#set($lowerTableInfo = $tool.firstLowerCase($!{tableInfo.name}))
##引入相应的包
#if($tableInfo.savePackageName)package $!{tableInfo.savePackageName}.#{end}service;
import cn.itsource.zx.domain.$!{tableInfo.name};
public interface I$!{tableInfo.name}Service extends IBaseService<$!{tableInfo.name},Long> {
}
Service
##定义初始变量
#set($tableName = $tool.append($tableInfo.name, "ServiceImpl"))
##设置回调
$!callback.setFileName($tool.append($tableName, ".java"))
$!callback.setSavePath($tool.append($tableInfo.savePath, "/main/java/cn/itsource/zx/service/impl"))
##拿到首字母小写的表单
#set($lowerTableInfo = $tool.firstLowerCase($!{tableInfo.name}))
##引入相应的包
#if($tableInfo.savePackageName)package $!{tableInfo.savePackageName}.#{end}service.impl;
import cn.itsource.zx.domain.$!{tableInfo.name};
import cn.itsource.zx.repository.$!{tableInfo.name}Repository;
import cn.itsource.zx.service.I$!{tableInfo.name}Service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class $!{tableInfo.name}ServiceImpl extends BaseServiceImpl<$!{tableInfo.name},Long> implements I$!{tableInfo.name}Service {
@Autowired
private $!{tableInfo.name}Repository $!{lowerTableInfo}Repository};
}
一,shiro
Apache Shiro是一个强大且易用的架,有身份验证、授权、密码学和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。
Spring security 重量级安全框架Java安全框
Apache Shiro轻量级安全框架
Shiro:是一个apache的安全框架,还有spring security;
Shiro:能做:身份认证(登录)authentication,授权authorization,密码学,会话管理。
应用–》Subject(当前用户)—》Security Mananger管理–》Realm----》db
导入jar
<dependencies>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
[users]
# 用户 'root' 密码是 'secret' and the 'admin' 角色
root = secret, admin
# 用户 'guest' 密码 'guest' 和'guest'角色
guest = guest, guest
# user 'presidentskroob' with password '12345' ("That's the same combination on
# my luggage!!!" ;)), and role 'president'
presidentskroob = 12345, president
# 用户 'darkhelmet' with password 'ludicrousspeed' and 角色 'darklord' and 'schwartz'
darkhelmet = 123, darklord, schwartz
# 用户 'lonestarr' 密码 'vespa' and roles 'goodguy' and 'schwartz'
lonestarr = vespa, goodguy, schwartz
[roles]
# 'admin' role has all permissions, indicated by the wildcard '*'
admin = *
# 这个 'schwartz' 角色 能干lightsaber下面的所有事情:
schwartz = lightsaber:*
# goodguy这个角色 能干winnebago下面的drive权限,操作eagle5这个资源 user:delete:5
goodguy = winnebago:drive:eagle5
测试
@Test
public void test() throws Exception{
//填入路径到工厂对象中去
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
//得到SecutiryManager 核心对象
SecurityManager securityManager = factory.getInstance();
//设置到shiro环境中去
SecurityUtils.setSecurityManager(securityManager);
//获得主体
Subject subject = SecurityUtils.getSubject();
//创建令牌
UsernamePasswordToken token = new UsernamePasswordToken("darkhelmet", "123");
try {
subject.login(token);
System.out.println("登陆成功");
} catch (UnknownAccountException e) {
e.printStackTrace();
System.out.println("该账号不存在");
}catch (IncorrectCredentialsException e) {
e.printStackTrace();
System.out.println("密码错误");
}catch (AuthenticationException e) {
e.printStackTrace();
System.out.println("其他认证错误");
}
//角色判断
if (subject.hasRole("admin")){
System.out.println("这个角色有admin");
}else{
System.out.println("没有该角色");
}
//权限判断
if (subject.isPermitted("lightsaber:*")){
System.out.println("该角色有lightsaber权限");
}else{
System.out.println("没有权限");
}
}
自定义Realm
public class MyRealm extends AuthorizingRealm {
//授权
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//得到用户名
String primaryPrincipal = (String) principalCollection.getPrimaryPrincipal();
//拿到权限去数据库查询
Set<String> role = getRoleByPrincipal(primaryPrincipal);
//拿到角色去数据库查询
Set<String> permission = getPermissionByPrincipal(primaryPrincipal);
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
//设置角色
simpleAuthorizationInfo.setRoles(role);
//设置权限
simpleAuthorizationInfo.setStringPermissions(permission);
return simpleAuthorizationInfo;
}
//通过用户拿到角色
private Set<String> getRoleByPrincipal(String primaryPrincipal){
Set<String> roles = new HashSet<String>();
if("gongji".equals(primaryPrincipal)){
roles.add("admin");
return roles;
}else{
return null;
}
}
//通过用户获取权限
private Set<String> getPermissionByPrincipal(String primaryPrincipal){
Set<String> permissions = new HashSet<String>();
if("gongji".equals(primaryPrincipal)){
permissions.add("admin");
return permissions;
}else{
return null;
}
}
//身份认证
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//得到令牌
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
//主体
Object principal = token.getPrincipal();
System.out.println(principal);
//获取用户名 zhangsan
String username = token.getUsername();
System.out.println(username);
//通过用户名 去数据库查询数据 -- 查询用户的信息
// 没有加密返回 String dbpwd= getPwdByUsername(username);
String md5Pwd = getMD5PwdByUsername(username);
//处理用户名问题 用户名是否存在的问题
if(md5Pwd==null){
//用户不存在
return null;
}
//ByteSource
ByteSource salt = ByteSource.Util.bytes("itsource");
//下面进行密码的比对 shiro 底层它会自动进行比对 -- 处理密码问题
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(username, md5Pwd,salt, getName());
return simpleAuthenticationInfo;
}
//通过用户名去拿到密码
public String getPwdByUsername(String username){
if("gongji".equals(username)){
return "123";
}else{
return null;
}
}
//加密返回密码
public String getMD5PwdByUsername(String username){
if("gongji".equals(username)){
return "d5a3fedf6c59c2ecbe7f7a6c1a22da37";
}else{
return null;
}
}
public static void main(String[] args) {
//MD5 本身是不可破解 ..不可逆 网上看到破解方式 都是穷举把
//(1)普通加密+202cb962ac59075b964b07152d234b70 -123
SimpleHash simpleHash = new SimpleHash("MD5","123" );
//(2)加密+加次数
SimpleHash simpleHash1 = new SimpleHash("MD5","123",null,10 );
//5371007260db2b98e3f7402395c45f28
//(3)加密123 加盐 + itsource +加次数 --d5a3fedf6c59c2ecbe7f7a6c1a22da37 -- 最安全
SimpleHash simpleHash2 = new SimpleHash("MD5","123","itsource",10 );
System.out.println(simpleHash.toString());
System.out.println(simpleHash1.toString());
System.out.println(simpleHash2.toString());
}
}
Shiro中密码加密
@Test
public void testDiyRealm() throws Exception{
//创建自定义myRealm
MyRealm myRealm = new MyRealm();
//得到SecutiryManager 核心对象
DefaultSecurityManager securityManager = new DefaultSecurityManager();
securityManager.setRealm(myRealm);
//设置到shiro的环境里面 才能运行
SecurityUtils.setSecurityManager(securityManager);
//密码匹配器
HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
matcher.setHashAlgorithmName("MD5");
matcher.setHashIterations(10);
//在realm设置匹配器
myRealm.setCredentialsMatcher(matcher);
//完成一些认证 拿到主体 -- 游客
Subject subject = SecurityUtils.getSubject();
//创建令牌
UsernamePasswordToken token = new UsernamePasswordToken("gongji","123");
try {
subject.login(token);
System.out.println("登陆成功");
} catch (UnknownAccountException e) {
e.printStackTrace();
System.out.println("用户不存在");
}catch (IncorrectCredentialsException e) {
e.printStackTrace();
System.out.println("密码错误..");
}catch (AuthenticationException e) {
e.printStackTrace();
System.out.println("其他认证错误");
}
System.out.println(subject);
//判断角色 --shiro底层 自动找到myRealm 返回AuthorizationInfo 信息 进行判断比较
if(subject.hasRole("admin")){
System.out.println("豪哥有:admin角色");
}
//判断主题是否有权限
if(subject.isPermitted("driver:xx")){
System.out.println("公鸡可以开车");
}
subject.logout();//退出
}
二,Shiro集成Spring
配置导包
<!-- shiro的支持包 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-all</artifactId>
<version>1.4.0</version>
<type>pom</type>
</dependency>
<!-- shiro与Spring的集成包 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
三,Authentication(身份认证)和Authorizing(授权)
public class MyRealm extends AuthorizingRealm {
//登陆认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//身份 username
Object username = authenticationToken.getPrincipal();
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
String username1 = token.getUsername();
//通过用户名得到密码
String pwd = getPwdByUsername(username1);
if(pwd == null){
return null;
}
//添加颜值
ByteSource salt = ByteSource.Util.bytes("itsource");
//得到simpleAuthenticationInfo
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(username,pwd,salt,getName());
return simpleAuthenticationInfo;
}
public String getPwdByUsername(String username){
if("bb".equals(username)){
return "607ca42819a4452f620bf721b7558c18";
}else{
return null;
}
}
public static void main(String[] args) {
SimpleHash simpleHash = new SimpleHash("MD5", "520520", "itsource", 10);
System.out.println(simpleHash);
}
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//得到主体/用户
String username =(String)principalCollection.getPrimaryPrincipal();
//根据用户 从数据库去拿到权限 加入到shiro里面
Set<String> permissions = new HashSet<>();
if(username.equals("bb")){
permissions.add("user:*");
permissions.add("employee:*");
}
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
simpleAuthorizationInfo.addStringPermissions(permissions);
//返回到shiro里面 就会进行权限的判断
return simpleAuthorizationInfo;
}
}
授权数据库查询
public class AisellFilterChainDefinitionMapBuilder {
public Map buildFilterChaiDefinitionMap(){
//map要顺序要求
/**
*/
Map mp = new LinkedHashMap();
mp.put("/s/login.jsp","anon");
mp.put("/login","anon");
mp.put("/s/permission.jsp","perms[user:*]");
mp.put("/**","authc");
return mp;
}
}
一,登录功能
把数据库里面的内容加密
public class MD5Util {
// String algorithmName, Object source, Object salt, int hashIterations
//设置盐值
public static final String SALT = "itsource";
//设置遍历次数
public static final int HASHITERATIONS = 10;
//传入一个字符串,返回一个md5编码的值
public static String createMd5Str(String str){
SimpleHash hash = new SimpleHash("MD5",str,SALT,HASHITERATIONS);
return hash.toString();
}
}
@Test
public void testUpdatePwd() throws Exception{
List<Employee> list = employeeService.findAll();
for (Employee employee : list) {
employee.setPassword(MD5Util.createMd5Str(employee.getPassword()));
employeeService.save(employee); //注:save是添加与修改
}
}
准备登陆页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>源码智销系统</title>
<%@ include file="/WEB-INF/views/head.jsp"%>
<script type="text/javascript">
//判断当前是否是顶层窗口
if (top != window){
window.top.location.href = window.location.href;
}
//登陆方法
function submitForm(){
//提交表单
$("#loginForm").form('submit',{
url:"/login",
success:function(data){
console.log(data);
console.log(typeof data);
var jsonObj = $.parseJSON(data);
console.log(jsonObj.success);
if(jsonObj.success){
//进入成功页面
location.href="/main/index";
}else{
$.messager.alert('温馨提示:',"登录失败"+jsonObj.msg,"error");
}
}
});
}
// //按回车进行登陆 绑定键盘事件 keyon keydown keyup 按完抬起
$(document.documentElement).on('keyup',function(event){
//event 键盘事件对象
if(event.keyCode == 13){
//提交表单
submitForm();
}
})
</script>
</head>
<body>
<div align="center" style="margin-top: 100px;">
<div class="easyui-panel" title="智销系统用户登陆" style="width: 350px; height: 240px;">
<form id="loginForm" class="easyui-form" method="post">
<table align="center" style="margin-top: 15px;">
<tr height="20">
<td>用户名:</td>
</tr>
<tr height="10">
<td><input name="username" class="easyui-validatebox" required="true" value="admin" /></td>
</tr>
<tr height="20">
<td>密 码:</td>
</tr>
<tr height="10">
<td><input name="password" type="password" class="easyui-validatebox" required="true" value="123456" /></td>
</tr>
<tr height="40">
<td align="center"><a href="javascript:;" class="easyui-linkbutton" onclick="submitForm();">登陆</a> <a
href="javascript:;" class="easyui-linkbutton" onclick="resetForm();">重置</a></td>
</tr>
</table>
</form>
</div>
</div>
</body>
</html>
修改applicationContext-shiro.xml
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"></property>
<!-- 认证没有成功跳转的页面-->
<property name="loginUrl" value="/login"></property>
<!-- 登陆成功进入页面-->
<property name="successUrl" value="/main/index"></property>
<!-- 没有权限 进入页面 -->
<property name="unauthorizedUrl" value="/s/unauthorized.jsp"></property>
静态资源放行
Map mp = new LinkedHashMap();
mp.put("/login","anon");
mp.put("*.js","anon");
mp.put("*.css","anon");
mp.put("/css/**","anon");
mp.put("/js/**","anon");
mp.put("/easyui/**","anon");
mp.put("/images/**","anon");
mp.put("/s/login.jsp","anon");
mp.put("/s/permission.jsp","perms[user:*]");
mp.put("/**","authc");
return mp;
完成登陆功能
public class JpaRealm extends AuthorizingRealm {
@Autowired
private IEmployeeService employeeService;
。。。
//AuthenticationInfo:认证; 身份验证; 证明
//登录的时候就会调用这个方法来做验证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//身份认证(用户名)
// 1.拿到用户名
UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken)token;
String username = usernamePasswordToken.getUsername();
// 2.根据用户名到数据库拿到用户
Employee loginUser = employeeService.findByUsername(username);
if(loginUser==null){
return null; //该用户名不存在
}
//从数据库中拿到密码
Object credentials = loginUser.getPassword();
//加盐的数据
ByteSource byteSource = ByteSource.Util.bytes("itsource");
return new SimpleAuthenticationInfo(loginUser,credentials,byteSource,getName());
}
}
json返回封装数据
public class JsonResult {
//是否成功
private boolean success = true;
//消息
private String msg ;
public JsonResult() {
}
public JsonResult(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;
}
}
登陆功能
@RequestMapping(value = "/login",method = RequestMethod.POST)
@ResponseBody
public JsonResult login(String username, String password) {
//得到主体
Subject subject = SecurityUtils.getSubject();
//判断改主体是否已经认证过
if (!subject.isAuthenticated()) {
//完成认证
try {
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
subject.login(token);
} catch (UnknownAccountException e) {
e.printStackTrace();
System.out.println("账号不存在");
return new JsonResult(false, "账号不存在");
} catch (IncorrectCredentialsException e) {
e.printStackTrace();
System.out.println("密码错误");
return new JsonResult(false, "密码错误");
} catch (AuthenticationException e) {
e.printStackTrace();
System.out.println("其他问题");
return new JsonResult(false, "其他问题");
}
}
//表示成功
return new JsonResult();
}
//注销
@RequestMapping("/logout")
public String logout(){
Subject subject = SecurityUtils.getSubject();
subject.logout();
return "forward:/WEB-INF/views/login.jsp";
}
二,权限domain部分
创建domain使用代码生成器完成
权限完善
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>permission管理</title>
<%@include file="/WEB-INF/views/head.jsp" %>
<script type="text/javascript" src="/js/model/permission.js"></script>
</head>
<body>
<table id="permissionGrid" class="easyui-datagrid" data-options="fit:true,fixed:true,fitColumns:true,toolbar:'#tb',singleSelect:true"
url="/permission/page"
iconCls="icon-save"
enableHeaderClickMenu="true"
rownumbers="true" pagination="true"
>
<thead>
<tr>
<th width="20" field="name" sortable="true">权限名称</th>
<th width="20" field="url" sortable="true">资源路径</th>
<th width="20" field="descs" sortable="true">描述</th>
<th width="20" field="sn" sortable="true">权限编号</th>
<%-- <th width="20" field="menuId" sortable="true">menuId</th>--%>
</tr>
</thead>
</table>
<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="edit" class="easyui-linkbutton" iconCls="icon-edit" plain="true">修改</a>
<a href="#" data-method="del" class="easyui-linkbutton" iconCls="icon-remove" plain="true">删除</a>
</div>
<!-- 这部门是查询的功能 -->
<div>
<form id="searchForm" action="/permission/download" method="post">
名称: <input name="name" class="easyui-textbox" style="width:80px;height:32px">
<a href="#" data-method="search" class="easyui-linkbutton" iconCls="icon-search">查找</a>
</form>
</div>
</div>
<!-- 弹出相应的功能框 -->
<div id="permissionDialog" class="easyui-dialog" title="数据操作" data-options="closed:true,modal:true" style="width:400px;padding:10px">
<form id="permissionForm" method="post">
<input id="permissionId" type="hidden" name="id">
<table cellpadding="5">
<tr>
<td>name:</td>
<td><input class="easyui-validatebox" type="text" name="name"></input></td>
</tr>
<tr>
<td>url:</td>
<td><input class="easyui-validatebox" type="text" name="url"></input></td>
</tr>
<tr>
<td>descs:</td>
<td><input class="easyui-validatebox" type="text" name="descs"></input></td>
</tr>
<tr>
<td>sn:</td>
<td><input class="easyui-validatebox" type="text" name="sn"></input></td>
</tr>
<tr>
<td>menuId:</td>
<td><input class="easyui-validatebox" type="text" name="menuId"></input></td>
</tr>
</table>
<div style="text-align:center;padding:5px">
<a href="javascript:void(0)" class="easyui-linkbutton" data-method="save">提交</a>
<a href="javascript:void(0)" class="easyui-linkbutton" onclick="$('#permissionDialog').dialog('close')">取消</a>
</div>
</form>
</div>
<div id="gridMenu" class="easyui-menu" style="width:120px;">
<div data-options="iconCls:'icon-add'" data-method="add" >添加</div>
<div data-options="iconCls:'icon-edit'" data-method="edit">修改</div>
<div data-options="iconCls:'icon-remove'" data-method="del">删除</div>
</div>
</body>
</html>
三,角色管理(页面功能)
<div id="roleDialog" class="easyui-dialog" data-options="closed:true,modal:true" title="功能操作" style="width:900px">
<div style="padding:10px 60px 20px 40px">
<form id="roleForm" class="easyui-form" method="post" data-options="">
<input type="hidden" id="roleId" name="id" >
<table cellpadding="5" style="width: 100%;">
<tr>
<td>
名称:<input class="easyui-validatebox" type="text" name="name" data-options="required:true"></input>
编码:<input class="easyui-validatebox" type="text" name="sn" data-options="required:true"></input>
</td>
</tr>
<tr>
<td colspan="2">
<div id="cc" class="easyui-layout" style="width: 100%;height:400px;">
<div data-options="region:'west',split:true" style="width:50%;">
<table id="userPermissionGrid">
<thead>
<tr>
<th width="10" field="name" >名称</th>
<th width="20" field="url" >对应的资源</th>
<th width="20" field="sn" >对象的权限</th>
</tr>
</thead>
</table>
</div>
<div data-options="region:'center'" style="padding:5px;background:#eee;">
<table id="allPermissionGrid">
<thead>
<tr>
<th width="10" field="name" >名称</th>
<th width="20" field="url" >对应的资源</th>
<th width="20" field="sn" >对象的权限</th>
</tr>
</thead>
</table>
</div>
</div>
</td>
</tr>
</table>
</form>
<div style="text-align:center;padding:5px">
<a href="javascript:void(0)" class="easyui-linkbutton" data-method="save">提交</a>
<a href="javascript:void(0)" class="easyui-linkbutton" onclick="$('#roleDialog').dialog('close')">取消</a>
</div>
</div>
</div>
role.js
…
//用户权限列表
var userPermissionGrid =$("#userPermissionGrid");
//所有权限列表
var allPermissionGrid =$("#allPermissionGrid");
…
var itsource={
… //添加数据(弹出添加的模态框)
add:function () {
//如果有data-save属性,我们把它展示出来
$("*[data-save]").show();
//启动有data-save属性的input元素的验证功能
$("*[data-save] input").validatebox("enableValidation");
roleForm.form("clear");//清除数据
//弹出表单窗口
roleDialog.dialog("center").dialog('open');
//当前用户权限清空
userPermissionGrid.datagrid("loadData",[]);
},
//修改数据(弹出修改的模态框)
edit:function () {
//选中了某一条数据才删除
var row = roleGrid.datagrid("getSelected");
if(row){
//隐藏有data-save属性的元素
$("*[data-save]").hide();
//禁用有data-save属性的input元素的验证功能
$("*[data-save] input").validatebox("disableValidation");
roleForm.form("clear");//清除数据
roleDialog.dialog("center").dialog('open');
//为form加载数据
roleForm.form("load",row);
//单独解决权限的回显问题(注意,这里要准备一个新的权限,以免删除时出问题)
var permissions = [];
$.extend(permissions,row.permissions);
userPermissionGrid.datagrid("loadData",permissions);
}else{
$.messager.alert('提示信息','请选择一行再进行修改!','info');
}
},
…
//双击所有权限,把一个权限交给这个角色
addPermission:function(rowIndex, rowData){
//判断是否有重复的行
//1.拿到角色权限所有的行数据
var rows = userPermissionGrid.datagrid("getRows");
//2.遍历rows拿到每一个员工数据
for(var i=0;i<rows.length;i++){
var row = rows[i]; //3.判断如果出现相等数据的情况
if(row.id == rowData.id){
//不做任何操作(也可以给出提示)
$.messager.show({
title:'提示',
msg:'该权限已经添加!',
timeout:2000,
showType:'slide'
});
return;
}
}
//把数据加到相应角色权限Grid中
userPermissionGrid.datagrid("appendRow",rowData);
},
//双击后移动相应的权限
removePermission:function (rowIndex, rowData) {
userPermissionGrid.datagrid("deleteRow",rowIndex);
}
}
//我们在这里创建两个Grid
//1.创建当前角色的权限Grid
userPermissionGrid.datagrid({
fit:true,
fixed:true,
fitColumns:true,
//双击后移除权限
onDblClickRow:itsource.removePermission
})
//2.获取所有权限的Grid
allPermissionGrid.datagrid({
fit:true,
fixed:true,
fitColumns:true,
//toolbar:'#tb',
// singleSelect:true";
url:"/permission/page",
rownumbers:"true" ,
pagination:"true",
//双击后添加权限
onDblClickRow:itsource.addPermission
})
一,权限判断
拿到所有用户
//登陆认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//身份 username
Object username = authenticationToken.getPrincipal();
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
String username1 = token.getUsername();
//通过用户名得到密码 admin 从数据库读取数据 --根据用户名去查询用户的方法
Employee employee = employeeService.getEmployeeByUsername(username1);
if(employee == null){
return null;
}
System.out.println(employee.getPassword());
//添加颜值
ByteSource salt = ByteSource.Util.bytes("itsource");
//得到simpleAuthenticationInfo
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(employee,employee.getPassword(),salt,getName());
return simpleAuthenticationInfo;
}
UserContext:设置与拿到当前登录用户
放入到session中去
//工具
public class UserContext {
public static final String USER_IN_SESSION = "loginUser";
//存入session
public static void setUser(Employee loginUser){
Subject subject = SecurityUtils.getSubject();
subject.getSession().setAttribute(USER_IN_SESSION, loginUser);
}
//取出session
public static Employee getUser(){
Subject subject = SecurityUtils.getSubject();
return (Employee) subject.getSession().getAttribute(USER_IN_SESSION);
}
}
根据当前用户拿到当前用户对应的id
public interface IPermissionService extends IBaseService<Permission,Long>{
//根据当前登录的用户 查询用户自己的权限
public Set<String> findPermissionsByLoginUser(Long id);
}
权限判断Ajax请求和自定义权限拦截器
public class AisellPermissionAuthorizationFilter extends PermissionsAuthorizationFilter {
//Ctrl+o复写方法
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
//得到主体
Subject subject = this.getSubject(request, response);
//判断主体是否为空
if(subject.getPrincipal()==null){
//登录失败的操作
this.saveRequestAndRedirectToLogin(request,response);
}else {
//如果拦截请求是ajax请求,就返回json处理 否者就返回页面
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp =(HttpServletResponse) response;
//获取请求头
String header = req.getHeader("X-Requested-With");
if ("XMLHttpRequest".equals(header)){
//设置类型和编码
resp.setContentType("text/json;charset=UTF-8");
resp.getWriter().print("{\"success\":false,\"msg\":\"没有权限\"}");
}else {
//返回页面
String unauthorizedUrl = this.getUnauthorizedUrl();
if (StringUtils.hasText(unauthorizedUrl)) {
WebUtils.issueRedirect(request, response, unauthorizedUrl);
} else {
WebUtils.toHttp(response).sendError(401);
}
}
}
return false;
}
}
静态资源放行
public class AisellFilterChainDefinitionMapBuilder {
//注入
@Autowired
private IPermissionService permissionService;
public Map buildFilterChaiDefinitionMap(){
//map要顺序要求
/**
*/
Map mp = new LinkedHashMap();
mp.put("/login","anon");
mp.put("*.js","anon");
mp.put("*.css","anon");
mp.put("/css/**","anon");
mp.put("/js/**","anon");
mp.put("/easyui/**","anon");
mp.put("/images/**","anon");
mp.put("/s/login.jsp","anon");
//从数据库查询出来放入shiro不能写死
List<Permission> permissions = permissionService.findAll();
//取出所有url和sn
for (Permission permission : permissions) {
String url = permission.getUrl();
String sn = permission.getSn();
mp.put(url,"aisellPerm["+sn+"]");
}
mp.put("/**","authc");
return mp;
}
}
二,菜单读取
menu
@Entity
@Table(name = "menu")
public class Menu extends BaseDomain{
private String name;
private String url;
private String icon;
//懒加载
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "parent_id")
//加载json时忽略
private Menu parent;
//表示临时的属性
@Transient
private List<Menu> children = new ArrayList<>();
下面是get,set
然后配置permission
//加载方式懒加载
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "menu_id")
private Menu menu;
MenuRepository
public interface MenuRepository extends BaseRepository<Menu,Long>{
@Query("select m from Employee e join e.roles er join er.permissions p join p.menu m where e.id = ?1 ")
public List<Menu> findMenuByLoginUser(Long userid);
}
MenuService
思路就是,判断每个不同用户下面有没有父菜单,有就添加子菜单,没有就添加父菜单
public class MenuServiceImpl extends BaseServiceImpl<Menu,Long> implements IMenuService {
@Autowired
private MenuRepository menuRepository;
@Override
public List<Menu> findMenuByLoginUser(Long userid) {
//查询所有子菜单
List<Menu> childrenList = menuRepository.findMenuByLoginUser(userid);
//创建一个集合放父菜单
List<Menu> perentList = new ArrayList<>();
//拿到所有菜单
for (Menu childrenMenu : childrenList) {
//得到父菜单
Menu parent = childrenMenu.getParent();
//判断时候有父菜单,没有就添加,有就添加子菜单
if (!perentList.contains(parent)){
perentList.add(parent);
}
parent.getChildren().add(childrenMenu);
}
return perentList;
}
}
MenuController
@RequestMapping("/findMenuByLoginUser")
@ResponseBody
public List<Menu> findMenuByLoginUser(){
Employee employee = UserContext.getUser();
//根据登陆用户的id 查询菜单
return menuService.findMenuByLoginUser(employee.getId());
}
一,配置使用poi的包
两款操作java操作办公框架
jxl: jxl早期只对excel操作,在处理excel上面有很多的优势 jxl在写入上要快一点
缺点:效率低,图片支持不完善,对格式的支持不如POI强大
poi:excel和word,ppt都可以才有 poi在读取上面要快一点
缺点:不成熟,代码不能跨平台,兼容性不是那么好
<!-- poi支持的jar包 -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.11</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.11</version>
</dependency>
使用poi完成九九乘法表
导出
@Test
public void testPoiWrite99() throws Exception{
//创建一个工作薄
XSSFWorkbook workbook = new XSSFWorkbook();
//创建一个表格
XSSFSheet sheet = workbook.createSheet("九九乘法表");
for (int i = 1; i <=9 ; i++) {
XSSFRow row = sheet.createRow(i-1);
for (int j = 1; j <= i ; j++) {
//创建一个单元格
XSSFCell cell = row.createCell(j-1);
cell.setCellValue(i+"*"+j+"="+(i*j));
}
}
//输出
FileOutputStream out = new FileOutputStream(
new File("99.xlsx"));
workbook.write(out);
out.close();
}
用poi导入办公文件
@Test
public void testPoiRead() throws Exception{
//读取
FileInputStream fis = new FileInputStream(
new File("empread.xlsx"));
//得到工作薄
XSSFWorkbook workbook = new XSSFWorkbook(fis);
//得到第一个表格
XSSFSheet spreadsheet = workbook.getSheetAt(0);
//获取表格所有的row
Iterator <Row> rowIterator = spreadsheet.iterator();
int count = 0;
//循环行
while (rowIterator.hasNext()) {
count++;
//取出具体哪一个行
XSSFRow row = (XSSFRow) rowIterator.next(); //取出两行进行抛弃
if (count > 2) {
//得到单元格
Iterator<Cell> cellIterator = row.cellIterator();
while (cellIterator.hasNext()) {
//取出单元格
Cell cell = cellIterator.next();
//判断单元格类型
switch (cell.getCellType()) {
case Cell.CELL_TYPE_NUMERIC:
System.out.print(
cell.getNumericCellValue() + " \t\t ");
break;
case Cell.CELL_TYPE_STRING:
System.out.print(
cell.getStringCellValue() + " \t\t ");
break;
}
}
System.out.println();
}
}
fis.close();
}
二,easypoi的基本使用
基本导出
@Test
public void testEasyuiPoi() throws Exception{
List<EasyEmployee> list = new ArrayList<>();
EasyEmployee easyEmployee1 = new EasyEmployee();
easyEmployee1.setName("草拟大爷");
easyEmployee1.setAge(30);
easyEmployee1.setSex(false);
easyEmployee1.setBirthday(new Date());
easyEmployee1.setHeadImage("4.bmp");
EasyEmployee easyEmployee2 = new EasyEmployee();
easyEmployee2.setName("小泽玛利亚");
easyEmployee2.setAge(20);
easyEmployee2.setSex(false);
easyEmployee2.setBirthday(new Date());
easyEmployee2.setHeadImage("6.bmp");
list.add(easyEmployee1);
list.add(easyEmployee2);
//导出代码
//ExportParams 设置标题 设置表名
Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams("av演员","女优"),
EasyEmployee.class, list);
//输出效果
FileOutputStream outputStream = new FileOutputStream(new File("employee导出.xlsx"));
workbook.write(outputStream);
outputStream.close();
}
测试关联对象,只导入部门和部门下面的东西
//关联对象 --表示这个对象里面也有导出的字段 深入导出
@ExcelEntity
private EasyDept easyDept;
测试代码
@Test
public void testEasyuiPoi2() throws Exception{
List<EasyDept> list = new ArrayList<>();
EasyDept easyDept1 = new EasyDept();
easyDept1.setName("无痛人流部");
EasyDept easyDept2 = new EasyDept();
easyDept2.setName("包皮包茎部");
list.add(easyDept1);
list.add(easyDept2);
//导出代码
//ExportParams 设置标题 设置表名
Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams("部门信息","部门"), EasyDept.class, list);
//输出效果
FileOutputStream outputStream = new FileOutputStream(new File("employee导出1.xlsx"));
workbook.write(outputStream);
outputStream.close();
}
解决图像问题
//2表示选择图片导出 1表示的文本
@Excel(name="头像",type = 2 ,width = 40 , height = 20)
测试代码
@Test
public void testEasyuiPoi1() throws Exception{
List<EasyEmployee> list = new ArrayList<>();
EasyEmployee easyEmployee1 = new EasyEmployee();
easyEmployee1.setName("草拟大爷");
easyEmployee1.setAge(30);
easyEmployee1.setSex(false);
easyEmployee1.setBirthday(new Date());
easyEmployee1.setHeadImage("4.bmp");
EasyDept dept1 = new EasyDept();
dept1.setName("男优部");
easyEmployee1.setEasyDept(dept1);
EasyEmployee easyEmployee2 = new EasyEmployee();
easyEmployee2.setName("小泽玛利亚");
easyEmployee2.setAge(20);
easyEmployee2.setSex(false);
easyEmployee2.setBirthday(new Date());
easyEmployee2.setHeadImage("6.bmp");
EasyDept dept2 = new EasyDept();
dept2.setName("女优部");
easyEmployee2.setEasyDept(dept2);
list.add(easyEmployee1);
list.add(easyEmployee2);
//导出代码
//ExportParams 设置标题 设置表名
Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams("av演员","女优"),
EasyEmployee.class, list);
//输出效果
FileOutputStream outputStream = new FileOutputStream(new File("employee导出.xlsx"));
workbook.write(outputStream);
outputStream.close();
}
三,和springmvc集成关联
首先配置Controller
测试代码(没有开启验证)
@Controller
@RequestMapping("/import")
public class ImportController {
//自定义验证类
@Autowired
private AisellEmployeeVerifyHander aisellEmployeeVerifyHander;
@Autowired
private IDepartmentService departmentService;
@Autowired
private IEmployeeService employeeService;
@RequestMapping("/index")
public String importPage(){
return "import";
}
//导入功能 --没有验证功能
@RequestMapping("/employeeXlsx1")
public String employeeXlsx1(MultipartFile empFile) throws Exception {
//这个文件怎么
ImportParams params = new ImportParams();
params.setTitleRows(1);
//导入
List<Employee> list = ExcelImportUtil.importExcel(empFile.getInputStream(),
Employee.class, params);
for (Employee employee : list) {
System.out.println(employee);
//根据部门名称查询的部门对象
String deptName = employee.getDepartment().getName();
Department department = departmentService.findDepartmentByName(deptName);
//设置部门
employee.setDepartment(department);
employee.setPassword("123456");
employeeService.save(employee);
}
return "import";
}
开启验证
//具备验证功能
@RequestMapping("/employeeXlsx")
public String employeeXlsx(MultipartFile empFile, HttpServletResponse response) throws Exception {
//这个文件怎么
ImportParams params = new ImportParams();
params.setTitleRows(1);
//开启验证
params.setNeedVerfiy(true);
//开启自定义验证
params.setVerifyHandler(aisellEmployeeVerifyHander);
//导入
ExcelImportResult<Employee> result = ExcelImportUtil.importExcelMore(empFile.getInputStream(),
Employee.class, params);
//正确数据
for (Employee employee : result.getList()) {
System.out.println(employee);
//根据部门名称查询的部门对象
String deptName = employee.getDepartment().getName();
Department department = departmentService.findDepartmentByName(deptName);
//设置部门
employee.setDepartment(department);
employee.setPassword("123456");
employeeService.save(employee);
}
//错误的数据
for (Employee employee : result.getFailList()) {
System.out.println("错误数据:"+employee);
}
//传回前台 让用户查看 修改
if(result.isVerfiyFail()){
//有时候信息
Workbook failWorkbook = result.getFailWorkbook();
//输出设置一堆参数
//把这个文件导出
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); //mime类型
response.setHeader("Content-disposition", "attachment;filename=error.xlsx");
response.setHeader("Pragma", "No-cache");//设置不要缓存
//输出内容
OutputStream ouputStream = response.getOutputStream();
failWorkbook.write(ouputStream);
ouputStream.flush();
ouputStream.close();
}
return "import";
}
最后自定义验证
public class AisellEmployeeVerifyHander implements IExcelVerifyHandler<Employee> {
@Autowired
private IEmployeeService employeeService;
@Override
public ExcelVerifyHandlerResult verifyHandler(Employee employee) {
ExcelVerifyHandlerResult result = new ExcelVerifyHandlerResult(true);
boolean flag = employeeService.checkUsername(employee.getUsername());
if(!flag){
result.setSuccess(false);
result.setMsg("用户名重复");
}
return result;
}
}
一,系统数据字典
SystemDictionaryType
@Entity
@Table(name = "systemdictionarydetail")
public class Systemdictionarydetail extends BaseDomain{
//定义两个常量
public static final String PRODUCT_BRAND = "productBrand";
public static final String PRODUCT_UNIT = "productUnit";
private String name;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "types_id")
private Systemdictionarytype types;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Systemdictionarytype getTypes() {
return types;
}
public void setTypes(Systemdictionarytype types) {
this.types = types;
}
}
query
public class SystemdictionarydetailQuery extends BaseQuery{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public Specification createSpecification() {
Specification<Systemdictionarydetail> spe = Specifications.<Systemdictionarydetail>and().
like(StringUtils.isNotBlank(name), "name", "%" + name + "%")
.build();
return spe;
}
}
Repository
public interface SystemdictionarydetailRepository extends BaseRepository<Systemdictionarydetail,Long>{
@Query("select d from Systemdictionarydetail d join d.types t where t.sn=?1")
List<Systemdictionarydetail> findDetailsBySn(String sn);
}
service
public interface ISystemdictionarydetailService extends IBaseService<Systemdictionarydetail,Long> {
/**
* 根据编号查询对应的数据字典明细
* @param sn 数据字典类型的编号
* @return
*/
List<Systemdictionarydetail> findDetailsBySn(String sn);
}
@Service
public class SystemdictionarydetailServiceImpl extends BaseServiceImpl<Systemdictionarydetail,Long> implements ISystemdictionarydetailService {
@Autowired
private SystemdictionarydetailRepository systemdictionarydetailRepository;
@Override
public List<Systemdictionarydetail> findDetailsBySn(String sn) {
return systemdictionarydetailRepository.findDetailsBySn(sn);
}
}
Controller
@Controller
@RequestMapping("/systemdictionarydetail")
public class SystemdictionarydetailController {
@Autowired
private ISystemdictionarydetailService systemdictionarydetailService;
@RequestMapping("/index")
public String index(){
return "systemdictionarydetail/systemdictionarydetail";
}
//写一个方法查询数据 返回json --查数据库 findPage
@RequestMapping("/page")
@ResponseBody
public UiPage page(SystemdictionarydetailQuery systemdictionarydetailQuery){
//List<Systemdictionarydetail> systemdictionarydetails = systemdictionarydetailService.findAll();
Page page = systemdictionarydetailService.findPageByQuery(systemdictionarydetailQuery);
UiPage uipage = new UiPage(page);
return uipage;
}
//方法 delete
@RequestMapping("/delete")
@ResponseBody
public Map delete(Long id){
Map mp = new HashMap();
try {
systemdictionarydetailService.delete(id);
mp.put("success", true);
} catch (Exception e) {
e.printStackTrace();
mp.put("success", false);
mp.put("msg", e.getMessage());
}
return mp;
}
//保存方法-- 修改和新增保存
@RequestMapping("/save")
@ResponseBody
public Map save(Systemdictionarydetail systemdictionarydetail){
Map mp = new HashMap();
try {
systemdictionarydetailService.save(systemdictionarydetail);
mp.put("success", true);
} catch (Exception e) {
e.printStackTrace();
mp.put("success", false);
mp.put("msg", e.getMessage());
}
return mp;
}
@RequestMapping("/update")
@ResponseBody
public Map update(@ModelAttribute("editSystemdictionarydetail") Systemdictionarydetail systemdictionarydetail){
Map mp = new HashMap();
try {
//Systemdictionarydetail systemdictionarydetail = findOne(systemdictionarydetail.getId())
systemdictionarydetailService.save(systemdictionarydetail);
mp.put("success", true);
} catch (Exception e) {
e.printStackTrace();
mp.put("success", false);
mp.put("msg", e.getMessage());
}
return mp;
}
//删除方法
@ModelAttribute("editSystemdictionarydetail")
public Systemdictionarydetail beforeEdit(Long id, String cmd){
Systemdictionarydetail systemdictionarydetail = null;
if("update".equals(cmd) && id != null && !"".equals(id) ){
systemdictionarydetail = systemdictionarydetailService.findOne(id);
}
return systemdictionarydetail;
}
}
二,自主完成CRUD
使用代码生成器完成:
domain
query
repository
service
controller
还有前台页面
产品页面展示
<table id="productGrid" class="easyui-datagrid" data-options="fit:true,fixed:true,fitColumns:true,toolbar:'#tb',singleSelect:true";
url="/product/page"
iconCls="icon-save"
rownumbers="true" pagination="true">
<thead>
<tr>
<th width="20" field="name" >名称</th>
<th width="20" field="color" data-options="formatter:formatColor" >颜色</th>
<th width="20" field="smallpic" data-options="formatter:formatImg" >图片</th>
<th width="20" field="costprice" >成本价</th>
<th width="20" field="saleprice" >销售价</th>
<th width="20" field="types" data-options="formatter:formatObj" >类型</th>
<th width="20" field="unit" data-options="formatter:formatObj" >单位</th>
<th width="20" field="brand" data-options="formatter:formatObj" >品牌</th>
</tr>
</thead>
</table>
处理大图展示方法
function loadSuccess(data) {
//看是否进来了
alert(进来了)
var rows = data.rows;
for(var i=0;i<rows.length;i++){
var result = rows[i];
.
e
a
s
y
u
i
.
t
o
o
l
t
i
p
.
i
n
i
t
(
.easyui.tooltip.init(
.easyui.tooltip.init((“img[src=’”+result.smallpic+"’]"), {
position: “rigth”,
content: “<div style=“width:600px;height:480px;”>”
});
}
}
添加与修改页面
<div id="productDialog" class="easyui-dialog" data-options="closed:true,modal:true" title="功能操作" style="width:450px">
<div style="padding:10px 60px 20px 40px">
<form id="productForm" class="easyui-form" method="post" data-options="">
<input type="hidden" id="productId" name="id" >
<table cellpadding="5">
<tr>
<td>名称:</td>
<td><input class="easyui-validatebox" type="text" name="name" data-options="required:true"></input></td>
</tr>
<tr>
<td>颜色:</td>
//注意颜色在回显的时候不用去写,,,type=color可以直接回显
<td><input class="easyui-validatebox" type="color" name="color"></input></td>
</tr>
<tr>
<td>成本价:</td>
<td><input class="easyui-validatebox" type="text" name="costprice"></input></td>
</tr>
<tr>
<td>销售价:</td>
<td><input class="easyui-validatebox" type="text" name="saleprice"></input></td>
</tr>
<tr>
<td>产品图片:</td>
<td>
<input name="fileImage" class="easyui-filebox" style="width:100%">
</td>
</tr>
</table>
</form>
<div style="text-align:center;padding:5px">
<a href="javascript:void(0)" class="easyui-linkbutton" data-method="save">提交</a>
<a href="javascript:void(0)" class="easyui-linkbutton" onclick="$('#productDialog').dialog('close')">取消</a>
</div>
</div>
</div>
产品修改时回显
edit:function () {
//选中了某一条数据才删除
var row = productGrid.datagrid("getSelected");
if(row){
//隐藏有data-save属性的元素
$("*[data-save]").hide();
//禁用有data-save属性的input元素的验证功能
$("*[data-save] input").validatebox("disableValidation");
productForm.form("clear");//清除数据
productDialog.dialog("center").dialog('open');
//解决单位,品牌,类型的回显问题
if(row.unit){
row["unit.id"] = row.unit.id;
}
if(row.brand){
row["brand.id"] = row.brand.id;
}
if(row.types){
row["types.id"] = row.types.id;
}
//为form加载数据
productForm.form("load",row);
}else{
$.messager.alert('提示信息','请选择一行再进行修改!','info');
}
},
保存
@RequestMapping("/save")
@ResponseBody
public Map<String,Object> save(Product product,HttpServletRequest req){
return saveOrUpdate(product,req);
}
最后删除(注意删除时把图片也删除了)
//删除功能
@RequestMapping("/delete")
@ResponseBody
public Map<String,Object> delete(Long id,HttpServletRequest req){
Map<String,Object> map = new HashMap<>();
try{
Product product = productService.findOne(id);
productService.delete(id);
// 删除图片的代码,写在delete方法之后
String webapp = req.getServletContext().getRealPath("/");
if (id != null && StringUtils.isNotBlank(product.getPic())) {
File deleteFile = new File(webapp, product.getPic());
if (deleteFile.exists()) {
deleteFile.delete();
}
File deleteSmallFile = new File(webapp, product.getSmallPic());
if (deleteSmallFile.exists()) {
deleteSmallFile.delete();
}
}
map.put(SUCCESS,true);
}catch (Exception e){
map.put(SUCCESS,false);
map.put("msg",e.getMessage());
e.printStackTrace();
}
return map;
}
一,采购单模型设计
判断数据库联系
如果是下拉列表:一般是多对一,一对一
如果是复选框:一般是多对多,一对多
采购单组合关系
主表
@Entity
@Table(name="purchasebill")
public class Purchasebill extends BaseDomain {
private Date vdate; // 交易时间 前台传过来
private BigDecimal totalAmount; //订单总金额 --后台计算出来
private BigDecimal totalNum; //订单数量 --后台计算出来
private Date inputtime = new Date();//采购单录入时间 -- 后台生成
private Date auditortime; //审核时间 --- 后台生成
private Integer status = 0; //单据状态 0 表示待审核 1 表示审核 -1表作废
//private Long supplierId;
@ManyToOne(fetch = FetchType.LAZY,optional = false)
@JoinColumn(name="supplier_id")
private Supplier supplier;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name="auditor_id")
private Employee auditor;//审核人 录入可以为null
@ManyToOne(fetch = FetchType.LAZY,optional = false)
@JoinColumn(name="inputUser_id")
private Employee inputUser;//录入人 不能为null 当前登陆用户
@ManyToOne(fetch = FetchType.LAZY,optional = false)
@JoinColumn(name="buyer_id")
private Employee buyer;//采购员
//明細 强级联 orphanRemoval 一方解除关系 去删除
@OneToMany(cascade = CascadeType.ALL,fetch = FetchType.LAZY,mappedBy = "bill",
orphanRemoval = true)
private List<Purchasebillitem> items = new ArrayList<>();
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
public Date getVdate() {
return vdate;
}
@DateTimeFormat(pattern = "yyyy-MM-dd")
public void setVdate(Date vdate) {
this.vdate = vdate;
}
public BigDecimal getTotalAmount() {
return totalAmount;
}
public void setTotalAmount(BigDecimal totalAmount) {
this.totalAmount = totalAmount;
}
public BigDecimal getTotalNum() {
return totalNum;
}
public void setTotalNum(BigDecimal totalNum) {
this.totalNum = totalNum;
}
public Date getInputtime() {
return inputtime;
}
public void setInputtime(Date inputtime) {
this.inputtime = inputtime;
}
public Date getAuditortime() {
return auditortime;
}
public void setAuditortime(Date auditortime) {
this.auditortime = auditortime;
}
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
public Supplier getSupplier() {
return supplier;
}
public void setSupplier(Supplier supplier) {
this.supplier = supplier;
}
public Employee getAuditor() {
return auditor;
}
public void setAuditor(Employee auditor) {
this.auditor = auditor;
}
public Employee getInputUser() {
return inputUser;
}
public void setInputUser(Employee inputUser) {
this.inputUser = inputUser;
}
public Employee getBuyer() {
return buyer;
}
public void setBuyer(Employee buyer) {
this.buyer = buyer;
}
public List<Purchasebillitem> getItems() {
return items;
}
public void setItems(List<Purchasebillitem> items) {
this.items = items;
}
}
采购明细表
@Entity
@Table(name="purchasebillitem")
public class Purchasebillitem extends BaseDomain {
private BigDecimal price;//产品价格
private BigDecimal num;//产品数量
private BigDecimal amount;//产品小计
private String descs; //产品描述
// private Long productId;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name="product_id")
private Product product;
//订单 bill_id
@ManyToOne(fetch = FetchType.LAZY,optional = false)
@JoinColumn(name="bill_id")
@JsonIgnore //返回页面 不展示出来
private Purchasebill bill;
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
public BigDecimal getNum() {
return num;
}
public void setNum(BigDecimal num) {
this.num = num;
}
public BigDecimal getAmount() {
return amount;
}
public void setAmount(BigDecimal amount) {
this.amount = amount;
}
public String getDescs() {
return descs;
}
public void setDescs(String descs) {
this.descs = descs;
}
public Product getProduct() {
return product;
}
public void setProduct(Product product) {
this.product = product;
}
public Purchasebill getBill() {
return bill;
}
public void setBill(Purchasebill bill) {
this.bill = bill;
}
}
订单明细表展示页面
<table id="purchasebillGrid" class="easyui-datagrid" data-options="fit:true,fixed:true,fitColumns:true,toolbar:'#tb',singleSelect:true";
url="/purchasebill/page"
iconCls="icon-save"
rownumbers="true" pagination="true">
<thead>
<tr>
<th width="20" field="vdate">交易时间</th>
<th width="20" field="supplier" data-options="formatter:formatObj">供应商</th>
<th width="20" field="buyer" data-options="formatter:formatEmp">采购员</th>
<th width="20" field="totalNum">总数量</th>
<th width="20" field="totalAmount" >总金额</th>
<th width="20" field="status" data-options="formatter:formatStatus" >状态</th>
</tr>
</thead>
</table>
处理明细单审核状态
function formatStatus(action) {
var data = {
0:"<div style='color:red;'>待审</div>",
1:"<div style='color: green'>已审</div>",
"-1":"<div><s>作废</s></div>"
};
return data[action];
}
高级查询,
因为只查询到集体的天数,所以只能查到一条数据,
因此我们把时间往后加一天处理,注意1.结束时间是不能成功获取查询的值.
如果是月底,时间会自动添加到下一个月
@Override
public Specification createSpecification() {
Date tempDate = null;
if(this.endDate!=null){
tempDate = DateUtils.addDays(this.endDate,1 );
System.out.println(tempDate);
}
Specification<Purchasebill> spe = Specifications.<Purchasebill>and().
ge(this.beginDate != null, "vdate",this.beginDate ).
lt(this.endDate!=null,"vdate",tempDate).
eq(this.status!=null && !"".equals(this.status),"status",this.status)
.build();
return spe;
}
弹出采购对话框
加载采购员 和 供应商
<tr>
<td>供应商:</td>
<td><input class="easyui-combobox" type="text" name="supplier.id"
data-options="panelHeight:'auto',valueField:'id',textField:'name',url:'/util/findAllSupplier'"
></input></td>
</tr>
<tr>
<td>采购员:</td>
<td><input class="easyui-combobox" type="text" name="buyer.id" data-options="valueField:'id',textField:'username',url:'/util/findAllBuyer'"></input></td>
</tr>
二,采购单明细表格处理(难点)
表单样式可以从网上下载,也可以应课件准备好的
<div id="purchasebillDialog" class="easyui-dialog" data-options="closed:true,modal:true" title="功能操作" style="width:800px">
<div style="padding:10px 60px 20px 40px">
<form id="purchasebillForm" class="easyui-form" method="post" data-options="">
<input type="hidden" id="purchasebillId" name="id" >
<table cellpadding="5">
<tr>
<td>交易时间:</td>
<td><input class="easyui-datebox"name="vdate" data-options="required:true"></input></td>
</tr>
<tr>
<td>供应商:</td>
<td>
<input class="easyui-combobox" name="supplier.id"
data-options="valueField:'id',textField:'name',panelHeight:'auto',url:'/util/findAllSupplier'">
</td>
</tr>
<tr>
<td>采购员:</td>
<td>
<input class="easyui-combobox" name="buyer.id"
data-options="valueField:'id',textField:'username',url:'/util/getBuyer'">
</td>
</tr>
</table>
</form>
<div style="text-align:center;padding:5px">
<a href="javascript:void(0)" class="easyui-linkbutton" data-method="save">提交</a>
<a href="javascript:void(0)" class="easyui-linkbutton" onclick="$('#purchasebillDialog').dialog('close')">取消</a>
</div>
</div>
</div>
明细单保存
准备额外参数
//保存方法 --提交表单的数据到后台
purchasebillForm.form('submit', {
url:url,
onSubmit: function(param){
//提交 封装 items
//得到明细表格所有的数据 price num descs
var rows = $("#gridItem").datagrid('getRows');
for(var i=0;i<rows.length;i++){
var rowData = rows[i];
param["items["+i+"].descs"] = rowData.descs;
param["items["+i+"].price"] = rowData.price;
param["items["+i+"].num"] = rowData.num;
param["items["+i+"].product.id"] = rowData.productId.id;
}
// 提交之前的验证
return purchasebillForm.form('validate');
解决修改回显问题
准备一个临时副本存放数据,
用的时候才拿出来,这样就不会出现明明没有删除操作,数据却显示不出来的问题
edit:function(){
//选择一条数据进行修改
var row = purchasebillGrid.datagrid('getSelected');
if(row){
//弹出对话框
purchasebillDialog.dialog('center').dialog('open');
//供货商
if(row.supplier){
row["supplier.id"] = row.supplier.id;
}
//供货商
if(row.buyer){
row["buyer.id"] = row.buyer.id;
}
for(var i=0;i<row.items.length;i++){
var item = row.items[i];
item["productId"] = item.product;
}
var items = $.extend([], row.items);
//明细表格回显
$("#gridItem").datagrid('loadData',items);
//修改 -- 回显示数据
purchasebillForm.form('load',row);
}else{
//提示用户
$.messager.alert('温馨提示:','请选中一条数据进行修改','info');
return;
}
最后解决n to n问题
设置关联对象为null,在每次用之前清理一次
就不会出现n-to-n问题了
purchasebill.setBuyer(null);
purchasebill.setSupplier(null);
purchasebill.getItems().clear();
一,采购单报表页面查询
datagrid
页面效果展示js
$(function(){
$('#purchasebillitemDatagrid').datagrid({
width:500,
height:250,
fit:true,
rownumbers:true,
remoteSort:false,
nowrap:false,
fitColumns:true,
toolbar:'#tb',
url:'/purchasebillitem/findAllItemVo',
columns:[[
{field:'id',title:'编号',width:100,sortable:true},
{field:'supplier',title:'供应商名称',width:80,align:'right',sortable:true},
{field:'buyer',title:'采购员名称',width:80,align:'right',sortable:true},
{field:'product',title:'产品名称',width:150,sortable:true},
{field:'productType',title:'产品分类',width:60,align:'center'},
{field:'vdate',title:'交易时间',width:180,align:'right',sortable:true},
{field:'num',title:'数量',width:50,sortable:true},
{field:'price',title:'价格',width:60,align:'center'},
{field:'amount',title:'小计',width:60,align:'center'},
{field:'status',title:'状态',width:60,align:'center',formatter:statusFormatter}
]],
groupField:'groupField',
view: groupview,
groupFormatter:function(value, rows){
var totalNum =0;
var totalAmount = 0;
//rows表示当前分组下面的行
for(var i=0;i<rows.length;i++){
var row = rows[i];//循环每一行
totalNum += row.num;
totalAmount += row.amount;
}
return value + ' - ' + rows.length + ' 条数据' +" <span style='color:green;'>共"+totalNum+"件商品</span>" +"<span style='color:#6b3980;'>总金额:"+totalAmount+"</span>";
}
});
});
后台数据
定义一个PurchaseBillItemVo 类
用来处理分组问题, 还有date时间问题,
用构造方法创建对象,并赋值
public class PurchasebillitemVo {
private Long id; //编号
private String supplier; //供应商名称
private String buyer; //采购员名称
private String product; //产品名称
private String productType; //产品分类
private Date vdate; //交易时间
private BigDecimal num; //采购数量
private BigDecimal price; //价格
private BigDecimal amount; //小计 = 价格*数量
private Integer status;
private String groupField = ""; //分组字段
//构造方法 创建对象或者赋值
public PurchasebillitemVo(Purchasebillitem item){
this.id = item.getId();
this.supplier = item.getBill().getSupplier().getName();
this.buyer = item.getBill().getBuyer().getUsername();
this.product = item.getProduct().getName();
this.productType = item.getProduct().getTypes().getName();
this.vdate = item.getBill().getVdate();
this.num = item.getNum();
this.price = item.getPrice();
this.amount = item.getAmount();
this.status = item.getBill().getStatus();
}
public PurchasebillitemVo(Purchasebillitem item,String groupBy){
this.id = item.getId();
this.supplier = item.getBill().getSupplier().getName();
this.buyer = item.getBill().getBuyer().getUsername();
this.product = item.getProduct().getName();
this.productType = item.getProduct().getTypes().getName();
this.vdate = item.getBill().getVdate();
this.num = item.getNum();
this.price = item.getPrice();
this.amount = item.getAmount();
this.status = item.getBill().getStatus();
//根据传入的条件 进行分组
if("o.bill.supplier.name".equals(groupBy)){
this.groupField = this.supplier;
}else if("o.bill.buyer.username".equals(groupBy)){
this.groupField = this.buyer;
}else if("MONTH(o.bill.vdate)".equals(groupBy)){
//this.groupField = this.vdate; 7 8
this.groupField = (DateUtils.toCalendar(this.vdate).get(Calendar.MONTH)+1)+"月";
}else{
this.groupField = this.supplier;
}
}
public static void main(String[] args) {
System.out.println(DateUtils.toCalendar(new Date()).get(Calendar.MONTH)+1);
}
public PurchasebillitemVo(){}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getSupplier() {
return supplier;
}
public void setSupplier(String supplier) {
this.supplier = supplier;
}
public String getBuyer() {
return buyer;
}
public void setBuyer(String buyer) {
this.buyer = buyer;
}
public String getProduct() {
return product;
}
public void setProduct(String product) {
this.product = product;
}
public String getProductType() {
return productType;
}
public void setProductType(String productType) {
this.productType = productType;
}
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
public Date getVdate() {
return vdate;
}
public void setVdate(Date vdate) {
this.vdate = vdate;
}
public BigDecimal getNum() {
return num;
}
public void setNum(BigDecimal num) {
this.num = num;
}
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
public BigDecimal getAmount() {
return amount;
}
public void setAmount(BigDecimal amount) {
this.amount = amount;
}
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
public String getGroupField() {
//设置分组字段 默认按照供应商分组
// this.groupField = this.supplier;
return groupField;
}
public void setGroupField(String groupField) {
this.groupField = groupField;
}
}
根据前台传来的日期,状态,和用户名来查询query
public class PurchasebillitemQuery extends BaseQuery{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//接收时间和状态
private Date beginDate;
private Date endDate;
private Integer status;
//介绍分组字段
private String groupBy = "o.bill.supplier.name";
public String getGroupBy() {
return groupBy;
}
public void setGroupBy(String groupBy) {
this.groupBy = groupBy;
}
public Date getBeginDate() {
return beginDate;
}
@DateTimeFormat(pattern = "yyyy-MM-dd")
public void setBeginDate(Date beginDate) {
this.beginDate = beginDate;
}
public Date getEndDate() {
return endDate;
}
@DateTimeFormat(pattern = "yyyy-MM-dd")
public void setEndDate(Date endDate) {
this.endDate = endDate;
}
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
//抽取查询
@Override
public Specification createSpecification() {
Date tempDate = null;
if(this.endDate!=null){
tempDate = DateUtils.addDays(this.endDate,1 );
System.out.println(tempDate);
}
//查询 Purchasebillitem select o from purchasebillitem o where o.bill.vdate = 1? and o.bill.status
Specification<Purchasebillitem> spe = Specifications.<Purchasebillitem>and().
ge(this.beginDate != null, "bill.vdate",this.beginDate ).
lt(this.endDate!=null,"bill.vdate",tempDate).
eq(this.status!=null && !"".equals(this.status),"bill.status",this.status)
.build();
return spe;
}
service层处理
@Autowired
private PurchasebillitemRepository purchasebillitemRepository;
@Override
public List<PurchasebillitemVo> findItems(PurchasebillitemQuery itemQuery) {
//查询所有的采购的明细
List<Purchasebillitem> items = purchasebillitemRepository.findByQuery(itemQuery);
List<PurchasebillitemVo> itemsVoList = new ArrayList<>();
for (Purchasebillitem item : items) {
PurchasebillitemVo itemVo = new PurchasebillitemVo(item,itemQuery.getGroupBy());
itemsVoList.add(itemVo);
}
return itemsVoList;
}
controller层处理
这里需要注意的是分组报表查询,因为前台页面效果是这样的
所以需要把查询出来的List放入map里去
//分组报表查询
@RequestMapping("/findAllItemVo")
@ResponseBody
public Map findPurchaseBillItemVo(PurchasebillitemQuery billitemQuery){
Map mp = new HashMap();
List<PurchasebillitemVo> items = purchasebillitemService.findItems(billitemQuery);
mp.put("total", items.size());
mp.put("rows", items);
return mp;
}
前台数据完成
引入js
<script type="text/javascript" src="/js/model/purchasebillitem.js"></script>
js
$(function(){
$('#purchasebillitemDatagrid').datagrid({
width:500,
height:250,
fit:true,
rownumbers:true,
remoteSort:false,
nowrap:false,
fitColumns:true,
toolbar:'#tb',
url:'/purchasebillitem/findAllItemVo',
columns:[[
{field:'id',title:'编号',width:100,sortable:true},
{field:'supplier',title:'供应商名称',width:80,align:'right',sortable:true},
{field:'buyer',title:'采购员名称',width:80,align:'right',sortable:true},
{field:'product',title:'产品名称',width:150,sortable:true},
{field:'productType',title:'产品分类',width:60,align:'center'},
{field:'vdate',title:'交易时间',width:180,align:'right',sortable:true},
{field:'num',title:'数量',width:50,sortable:true},
{field:'price',title:'价格',width:60,align:'center'},
{field:'amount',title:'小计',width:60,align:'center'},
{field:'status',title:'状态',width:60,align:'center',formatter:statusFormatter}
]],
groupField:'groupField',
view: groupview,
groupFormatter:function(value, rows){
var totalNum =0;
var totalAmount = 0;
//rows表示当前分组下面的行
for(var i=0;i<rows.length;i++){
var row = rows[i];//循环每一行
totalNum += row.num;
totalAmount += row.amount;
}
return value + ' - ' + rows.length + ' 条数据' +" <span style='color:green;'>共"+totalNum+"件商品</span>" +"<span style='color:#6b3980;'>总金额:"+totalAmount+"</span>";
}
});
});
二,HighChart,全球使用,echart(百度开发,开源免费)
引入,HighChart
3D报表图,2D只需改变 alpha到0度就可以了
charts3D:function(){
//发送ajax请求到后台查询的数据吧
var param = searchForm.serializeObject();
$.post("/purchasebillitem/findAllGraphic",param,function(result){
Highcharts.chart('purchasebillitemDialog', {
chart: {
type: 'pie',
options3d: {
enabled: true,
alpha: 45, //倾斜角度
beta: 0
}
},
title: {
text: '消费图'
},
tooltip: {
pointFormat: '{series.name}: <b>{point.percentage:.1f}%</b>'
},
plotOptions: {
pie: {
allowPointSelect: true,
cursor: 'pointer',
depth: 35,//深度
dataLabels: {
enabled: true,
format: '{point.name}'
}
}
},
series: [{
type: 'pie',
name: '消费比例',
data:result
}]
});
})
//3D图表 --打开对话框
purchasebillitemDialog.dialog('center').dialog('open');
通过文档看到其展示数据的类型是[{},{},{}]这样,所以我们需要相应的查询语句
拼接sql语句
//定义一个容器
List params = new ArrayList<>();
public String getWhereSql(){
String sql = "";
if(beginDate != null && !"".equals(beginDate)){
sql += " and b.vdate >= ?";
params.add(beginDate);
}
if(endDate != null && !"".equals(endDate)){
sql += " and b.vdate < ?";
params.add(endDate);
}
if(status != null && !"".equals(status)){
sql += " and b.status = ?";
params.add(status);
}
return sql.replace("and", "where");
}
public List getParams() {
return params;
}
public void setParams(List params) {
this.params = params;
}
查询到了图标需要的值后,在把数据弄成我们想要的样子
这里要注意jpql版本不同的bug,//JPA框架的bug的
group by的语句 不要写? group by 直接就拼接sql,会导致数据的丢失,
查询不出来
//根据jpql语句查询的数据
@Override
public List findItemsByJql(PurchasebillitemQuery itemQuery) {
List<Map> mpList = new ArrayList<>();
String groupBy = itemQuery.getGroupBy();
//根据jpql语句查询
String jpql = "select "+groupBy+",sum(b.totalAmount) from Purchasebillitem o join o.bill b "+itemQuery.getWhereSql()+" group by "+groupBy;
//查询出来的list list->数组 list.toArray()
List<Object[]> list = purchasebillitemRepository.findByJpql(jpql,itemQuery.getParams().toArray());
String jpql1 = "select o.bill.supplier.name,sum(b.totalAmount) from Purchasebillitem o join o.bill b group by ?";
List<Object[]> list1 = purchasebillitemRepository.findByJpql(jpql1,groupBy);
System.out.println("list1:"+list1);
for (Object[] objects : list) {
Object name = objects[0];
BigDecimal y = (BigDecimal)objects[1];
Map mp = new HashMap();
mp.put("name", name);
mp.put("y", y);
mpList.add(mp);
}
return mpList;
}
最后controlller层处理
//查询图形报表
@RequestMapping("/findAllGraphic")
@ResponseBody
public List findAllGraphic(PurchasebillitemQuery billitemQuery){
List itemsListMap = purchasebillitemService.findItemsByJql(billitemQuery);
return itemsListMap;
}
一,整个项目的基本流程
基础数据模块:,产品类型,供应商(采购),客户(销售)
权限管理 产品 数据字典 员工 部门–shiro(身份认证和授权)
采购模块
请购单录入
采购请购是指企业内部向采购部门提出采购申请,或采购部门汇总企业内部采购需求提出采购清单。
在采购管理模块——请购——请购单中,点击增加,出现空白单据
在单据中录入请购部门、请购人员、存货编码、数量等,确认无误后保存单据; 2、请购单审核
打开请购单列表弹出查询条件选择,录入过滤条件点击确定
程序将符合条件的单据过滤出来然后选择要审核的单据进行审核
库存模块
入库数据: 保存(stockincomebill/stockincomebillitem)
出库数据:
入库审核:
审核完之后, 进入productstock(即时库存表)和 depot(仓库表) 同时 (stockincomebill)入库单据状态变成已审核
出库审核之后:
审核完之后, 进入productstock和 depot 同时 (stockincomebill)入库单据状态变成已审核
盘点模块:(提了需求 crud)
清点模块 – 清点单(每个月有那么一两天清点)
盘盈盘亏数据 --单据
清点完之后–还有同步数据
及时库存表:(账面库存)
同一个仓库里面的产品是唯一
合并原则:(入库 通过同一个产品 同一个仓库) – 价格(加权平均法)
数量*价格 +库存数量 *价格 /数量+库存数量
保存入库–和采购单保存一样的
审核入库单(重点)审核流程:
单据变成已审核 ,改变状态 添加审核人 审核时间
更新即时库存表
更新仓库表
所以我们需要配置三张表课件上有拷过来直接改
@Entity
@Table(name="stockincomebill")
public class Stockincomebill extends BaseDomain {
private Date vdate;// 交易时间
private BigDecimal totalAmount;//总金额
private BigDecimal totalNum;//总数量
private Date inputTime = new Date();//入库事件
private Date auditorTime;//审核时间
private Integer status = 0;
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "supplier_id")
private Supplier supplier;// 多对一,非空
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "auditor_id")
private Employee auditor;// 审核人 多对一,可以为空
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "inputUser_id")
private Employee inputUser;// 入库人员 多对一,非空
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "keeper_id")
private Employee keeper;// 多对一,非空 //仓管员
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "depot_id")
private Depot depot;// 多对一,非空
// 一般组合关系使用List
@OneToMany(cascade = CascadeType.ALL, mappedBy = "bill", fetch = FetchType.LAZY, orphanRemoval = true)
private List<Stockincomebillitem> items = new ArrayList<Stockincomebillitem>();
@Entity
@Table(name = "stockincomebillitem")
public class Stockincomebillitem extends BaseDomain {
private BigDecimal price;
private BigDecimal num;
private BigDecimal amount;
private String descs;
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "product_id")
private Product product;// 多对一,非空
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "bill_id")
private Stockincomebill bill;// 组合关系,非空
仓库
@Entity
@Table(name="depot")
public class Depot extends BaseDomain {
private String name;
private BigDecimal maxcapacity;
private BigDecimal currentcapacity;
private BigDecimal totalamount;
public Depot(long deptid) {
this.id = deptid;
}
public Depot(){}
保存操作,配置了强级联
测试下
@Test
public void save() throws Exception {
//创建入库主单
Stockincomebill bill = new Stockincomebill();
bill.setDepot(new Depot(1L));
//入库人员 登陆人员 入库 (仓库管理人员 --登陆人员)
bill.setInputUser(new Employee(1L));
//入库人员 登陆人员 入库 (仓库管理人员)
bill.setKeeper(new Employee(2L));
//供货商
bill.setSupplier(new Supplier(2L));
//交易时间(入库事件) -- 在表里面添加字段 入库时间
bill.setVdate(new Date());
List<Stockincomebillitem> items = new ArrayList<Stockincomebillitem>();
Stockincomebillitem billItem = new Stockincomebillitem();
billItem.setDescs("备注1");
billItem.setNum(new BigDecimal(1));
billItem.setPrice(new BigDecimal(1));
billItem.setProduct(new Product(1L));
items.add(billItem);
Stockincomebillitem billItem2 = new Stockincomebillitem();
billItem2.setDescs("备注2");
billItem2.setNum(new BigDecimal(2));
billItem2.setPrice(new BigDecimal(2));
billItem2.setProduct(new Product(2L));
items.add(billItem2);
BigDecimal totalAmount = new BigDecimal(0);// 总金额
BigDecimal totalNum = new BigDecimal(0);// 总数量
for (Stockincomebillitem item : items) {
// 设置多方到一方的关系 --双向关系 级联保存
item.setBill(bill);
// 计算小计 price*num
item.setAmount(item.getPrice().multiply(item.getNum()));
// 累加
totalAmount = totalAmount.add(item.getAmount());
totalNum = totalNum.add(item.getNum());
}
// 设置总金额,总数量
bill.setTotalAmount(totalAmount);
bill.setTotalNum(totalNum);
// 设置一方到多方的关系
bill.setItems(items);
// 级联保存
stockincomebillService.save(bill);
接下来是审核操作
@Transactional
public void auditStockincomebill(Long billid, Employee auditor){
//需要审核单据
Stockincomebill bill = stockincomebillRepository.findOne(billid);
if(bill == null){
throw new RuntimeException("该单据不存在");
}
//0表示 待审 1 表示已审 -1表示作废
if(bill.getStatus() == 1){
throw new RuntimeException("该单据已审核");
}
if(bill.getStatus() == -1){
throw new RuntimeException("该单据已作废");
}
//单据里面 里面审核的时间 审核人 状态
bill.setAuditorTime(new Date());
bill.setAuditor(auditor);
bill.setStatus(1);
//保存数据 更新数据
stockincomebillRepository.save(bill);
// (2)仓库变化
Depot depot = bill.getDepot();
//当前容量
depot.setCurrentcapacity(depot.getCurrentcapacity().add(bill.getTotalNum()));
depot.setTotalamount(depot.getTotalamount().add(bill.getTotalAmount()));
//保存仓库
depotRepository.save(depot);
//(3)及时库存表变化 同一个仓库 同一个产品合并
// 第一次入及时库存 直接新增 如果不是第一次 需要进行合并
List<Stockincomebillitem> items = bill.getItems();
//查询当前这个产品在即时库存表离是否存在
String jpql = "select o from Productstock o where o.product=? and o.depot=?";
for (Stockincomebillitem item : items) {
Product product = item.getProduct();
List productStockList = productstockRepository.findByJpql(jpql, product, depot);
BigDecimal totalNum = new BigDecimal("0");
BigDecimal totalAmount = new BigDecimal("0");
if(productStockList.size() == 0){
//第一次 直接新增
Productstock productStock = new Productstock();
productStock.setDepot(depot);//仓库
productStock.setNum(item.getNum());
productStock.setPrice(item.getPrice());
productStock.setAmount(item.getAmount());
productStock.setIncomedate(new Date());//入即时库存时间
productStock.setProduct(product);//产品
productstockRepository.save(productStock);
}else if(productStockList.size() == 1){
//合并
Productstock productstock = (Productstock)productStockList.get(0);
totalNum = productstock.getNum().add(item.getNum());
totalAmount = productstock.getAmount().add(item.getAmount());
//总数量
productstock.setNum(productstock.getNum().add(item.getNum()));
//总金额
productstock.setAmount(productstock.getAmount().add(item.getAmount()));
//加权平均价格 --四舍五入
productstock.setPrice(totalAmount.divide(totalNum,2,BigDecimal.ROUND_HALF_EVEN));
productstockRepository.save(productstock);
}else{
throw new RuntimeException("仓库出问题");
}
}
测试审核
//测试审核
@Test
public void testAuditor() throws Exception{
stockincomebillService.auditStockincomebill(1L, new Employee(1));
}
二,库存预警和发送邮件
Java自带的timer不好做复杂的预警,所以
我们用了一个框架叫Qz
导包,配置文件
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>quartz</groupId>
<artifactId>quartz</artifactId>
<version>1.5.2</version>
</dependency>
引入配置交给spring管理
<?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:tx="http://www.springframework.org/schema/tx"
xmlns:task="http://www.springframework.org/schema/task"
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/task http://www.springframework.org/schema/task/spring-task.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- cron表达式:在每天早上8点到晚上8点期间每1分钟触发一次 -->
<!--value>0 0/1 8-20 * * ?</value -->
<!-- cron表达式:每5分钟触发一次 -->
<!-- <value>0 0/5 * * * ?</value> -->
<task:scheduled-tasks>
<!-- 执行quartzJob里面的work方法,执行频率是cron表达式 -->
<task:scheduled ref="quartzJob" method="work" cron="0 0/1 * * * ?" />
</task:scheduled-tasks>
</beans>
简单邮件,复杂邮件
引入配置,交给spring管理
<?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:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
<!-- 163邮箱,smtp.163.com -->
<!-- admin@163.com 用户名:admin 授权码:xxx -->
<!-- smtp邮件发送协议 -->
<!-- pop3收邮件协议 -->
<property name="host" value="smtp.163.com" />
<property name="username" value="zytestitsource" />
<property name="password" value="zytest8888" />
<property name="javaMailProperties">
<props>
<!-- 必须进行授权认证,它的目的就是阻止他人任意乱发邮件 -->
<prop key="mail.smtp.auth">true</prop>
<!-- SMTP加密方式:连接到一个TLS保护连接 -->
<prop key="mail.smtp.starttls.enable">true</prop>
</props>
</property>
</bean>
</beans>
测试
public class MailTest extends BaseTest {
//注入一个对象
/* @Autowired
JavaMailSender mailSender;
//简单发送邮件
@Test
public void testName() throws Exception {
//JavaMailSenderImpl xxx = (JavaMailSenderImpl)mailSender
// 简单邮件对象
SimpleMailMessage msg = new SimpleMailMessage();
// 发送人:和配置一致
msg.setFrom("1141275667@qq.com");
// 收件人
msg.setTo("876009440@qq.com");
//抄送其他人
String[] strs = new String[]{"1127560384@qq.com","763379508@qq.com","937783535@qq.com","346820640@qq.com"};
msg.setCc(strs);
// 主题
msg.setSubject("好热哦");
// 内容
msg.setText("好想去 游泳");
// 设置固定回邮地址
//msg.setReplyTo("xxxx@xxx.com");
// 发送
mailSender.send(msg);
}
*//**
* 发送复杂邮件
*//*
@Test
public void testSendMsg2() throws MessagingException {
//创建复杂的邮件发送
MimeMessage mimeMessage = mailSender.createMimeMessage();
//复杂邮件的工具类 设置编码格式
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage,true,"UTF-8");
helper.setFrom("1141275667@qq.com");
helper.setTo("876009440@qq.com");
helper.setSubject("热热热热热");
helper.setText("<h1>这是主题</h1>",true);
//发送附件
helper.addAttachment("热成狗.jpg",new File("E:/b3317f52.bmp"));
mailSender.send(mimeMessage);
}*/
}