项目二总结

1.使用到的框架
Spring +SpringMVC +SpringDataJpa+Maven+Easyui
2.使用到的技术点
一.SpringDataJpa(Spring封装了Jpa)
底层自动完成增删改查

@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);
}

二.集成Spring +SpringMVC +SpringDataJpa

<?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:jpa="http://www.springframework.org/schema/data/jpa"
       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/tx
         http://www.springframework.org/schema/tx/spring-tx.xsd
         http://www.springframework.org/schema/data/jpa
         http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">

    <!-- 扫描service部分的包 purchase(进货) sale(销售) store(库存) yxb 易销宝 aisell智能销售-->
    <context:component-scan base-package="cn.itsource.aisell.service" />
    <context:property-placeholder location="classpath:jdbc.properties" />
    <!-- 配置连接池 -->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <!--连接数据4个属性 -->
        <property name="driverClassName" value="${jdbc.driverClassName}" />
        <property name="url" value="${jdbc.url}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
        <!--maxActive: 最大连接数量 -->
        <property name="maxActive" value="150" />
        <!--minIdle: 最小空闲连接 -->
        <property name="minIdle" value="5" />
        <!--maxIdle: 最大空闲连接 -->
        <property name="maxIdle" value="20" />
        <!--initialSize: 初始化连接 -->
        <property name="initialSize" value="30" />
        <!-- 用来配置数据库断开后自动连接的 -->
        <!-- 连接被泄露时是否打印 -->
        <property name="logAbandoned" value="true" />
        <!--removeAbandoned: 是否自动回收超时连接 -->
        <property name="removeAbandoned" value="true" />
        <!--removeAbandonedTimeout: 超时时间(以秒数为单位) -->
        <property name="removeAbandonedTimeout" value="10" />
        <!--maxWait: 超时等待时间以毫秒为单位 1000等于60-->
        <property name="maxWait" value="1000" />
        <!-- 在空闲连接回收器线程运行期间休眠的时间值,以毫秒为单位. -->
        <property name="timeBetweenEvictionRunsMillis" value="10000" />
        <!-- 在每次空闲连接回收器线程(如果有)运行时检查的连接数量 -->
        <property name="numTestsPerEvictionRun" value="10" />
        <!-- 1000 * 60 * 30 连接在池中保持空闲而不被空闲连接回收器线程 -->
        <property name="minEvictableIdleTimeMillis" value="10000" />
        <property name="validationQuery" value="SELECT NOW() FROM DUAL" />
    </bean>

    <!-- 集成hibernate的jpa功能 -->
    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <!--待扫描的实体类包,不再需要persistence.xml了 -->
        <property name="packagesToScan" value="cn.itsource.aisell.domain" />
        <!-- 3.配置JPA的实现 -->
        <!-- private JpaVendorAdapter jpaVendorAdapter; -->
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <!-- org.springframework.orm.jpa.vendor.AbstractJpaVendorAdapter -->
                <!-- private boolean showSql = false;是否显示sql语句 -->
                <property name="showSql" value="true" />
                <!-- private boolean generateDdl = false;是否建表 -->
                <property name="generateDdl" value="false" />
                <!-- private String databasePlatform;原来方言 -->
                <property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect" />
            </bean>
        </property>
    </bean>

    <!-- Jpa 事务配置 -->
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory" />
    </bean>
    <!-- 注解声明式事务管理 -->
    <tx:annotation-driven />

    <!-- Spring Data Jpa配置 ********************************************-->
    <!-- base-package:扫描的包 dao层 -->
    <jpa:repositories base-package="cn.itsource.aisell.repository" transaction-manager-ref="transactionManager"
                      entity-manager-factory-ref="entityManagerFactory"
                      factory-class="cn.itsource.aisell.repository.BaseRepositoryFactoryBean"/>
    <!--加载shiro的配置文件-->
    <import resource="applicationContext-shiro.xml"></import>
    <import resource="classpath:applicationContext-qz.xml"></import>
    <import resource="classpath:applicationContext-email.xml"></import>
</beans>
<?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" xmlns:p="http://www.springframework.org/schema/p"
       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" />
    <!-- 添加mvc对@RequestMapping等注解的支持 -->
    <mvc:annotation-driven>
        <mvc:message-converters>
            <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
                <property name="supportedMediaTypes">
                    <list>
                        <value>application/json; charset=UTF-8</value>
                        <value>application/x-www-form-urlencoded; charset=UTF-8</value>
                    </list>
                </property>
                <!-- No serializer:配置 objectMapper 为我们自定义扩展后的 CustomMapper,解决了返回对象有关系对象的报错问题 department(额外的属性 handler) -->
                <property name="objectMapper">
                    <bean class="cn.itsource.aisell.common.CustomMapper"></bean>
                </property>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>

    <!-- 扫描easypoi的一些view:视图 -->
    <context:component-scan base-package="cn.afterturn.easypoi.view" />
    <context:component-scan base-package="cn.itsource.aisell.common"/>
    <!-- bean的视图解析器  p:order="0":顺序在最前面 -->
    <bean id="beanNameViewResolver" class="org.springframework.web.servlet.view.BeanNameViewResolver"
          p:order="0" />

    <!-- 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>

三.基本的CRUD(layer,jquery.form…) -> Ajax(局部刷新)
解决数据丢失问题

package cn.itsource.aisell.web.controller;

import cn.afterturn.easypoi.entity.vo.NormalExcelConstants;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import cn.itsource.aisell.domain.Employee;
import cn.itsource.aisell.query.BaseQuery;
import cn.itsource.aisell.query.EmployeeQuery;
import cn.itsource.aisell.service.IEmployeeService;
import cn.itsource.aisell.util.AjaxResult;
import cn.itsource.aisell.util.PageUi;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Controller
@RequestMapping("/employee")
public class EmployeeController {

    @Autowired
    private IEmployeeService employeeService;

    @RequestMapping("/index")
    public String index(){
        return "employee";
    }

    //查询数据 /employee/page
    //{total:111,rows:[{},{}]}
    @RequestMapping("/page")
    @ResponseBody
    public PageUi<Employee> page(EmployeeQuery employeeQuery){
        //查询数据 分页查询方法
       // List<Employee> employees = employeeService.findAll();
        Page page = employeeService.findPageByQuery(employeeQuery);
        //封装返回对象 必须要 total rows
        PageUi pageUi = new PageUi(page);
        return pageUi;
    }

    //删除失败 {success:false,msg:'成功'} AjaxResult
    /*@RequestMapping("/delete")
    @ResponseBody
    public Map delete(Long id){
        Map mp = new HashMap();
        try {
            employeeService.delete(id);
            mp.put("success", true);
            mp.put("msg", "操作成功");
        } catch (Exception e) {
            e.printStackTrace();
            mp.put("success", false);
            mp.put("msg", e.getMessage());

        }
        return mp;

    }*/

    @RequestMapping("/delete")
    @ResponseBody
    public AjaxResult delete(Long id){

        try {
            employeeService.delete(id);
            return new AjaxResult();
        } catch (Exception e) {
            e.printStackTrace();
           return new AjaxResult("操作失败"+e.getMessage());
        }

    }

    //写个保存方法
    @RequestMapping("/save")
    @ResponseBody
    public AjaxResult save(Employee employee){
        return  saveOrUpdate(employee);
    }


    @RequestMapping("/update")
    @ResponseBody
    //把查询出来的老数据和新数据进行叠加 新数据会覆盖老数据,没有接收,就不会覆盖
    public AjaxResult update(@ModelAttribute("editEmployee") Employee employee){
        //修改代码
      return  saveOrUpdate(employee);
    }
    public AjaxResult saveOrUpdate(Employee employee){
        try {
            employeeService.save(employee);
            return new AjaxResult();
        } catch (Exception e) {
            e.printStackTrace();
            return new AjaxResult("操作失败"+e.getMessage());
        }
    }
    //每次访问controller数据的时候都要进入该方法
    //修改
    @ModelAttribute("editEmployee")
    public Employee beforeEdit(Long id,String cmd){
        System.out.println("---------------------");
        if(id != null && "update".equals(cmd)){
            //根据id 查询老数据  删除和修改都要id值,只需要修改进入
            Employee employee = employeeService.findOne(id);
            //清空关联对象 -- 记住 以后遇到n-to-n 把关联对象清空 -- 记住这句话 保永生
            employee.setDepartment(null);
            return employee;
        }else{
            return null;
        }
    }

    //下载方法 download
    @RequestMapping("/download")
    //参1:前台传输的username passsword 需要高级查询不分页方法 findByQuery
    //惨2:模型ModelMap
    //参3:req图片保存路径
    public String download(EmployeeQuery employeeQuery, ModelMap map, HttpServletRequest req){
        //根据查询 查询数据
        List<Employee> employees = employeeService.findByQuery(employeeQuery);
        String realPath = req.getServletContext().getRealPath("");//E:\JavaEEIdeacode\aisell\src\main\web/images/head/avatar1.jpg
        employees.forEach(e->{
            //E:\JavaEEIdeacode\aisell\out\artifacts\aisell_Web_exploded\images\head
            e.setHeadImage(realPath+e.getHeadImage());
            System.out.println(e.getHeadImage());
        });
        //导出参数设置
        //E:\JavaEEIdeacode\aisell\src\main\web\images\head\sss.jpg
        ExportParams params = new ExportParams("员工数据", "员工表格1", ExcelType.XSSF);
        //固定两列的意思
        params.setFreezeCol(2);
        map.put(NormalExcelConstants.DATA_LIST, employees); // 数据集合
        map.put(NormalExcelConstants.CLASS, Employee.class);//导出实体
        map.put(NormalExcelConstants.PARAMS, params);//参数
        map.put(NormalExcelConstants.FILE_NAME, "xxx");//文件名称
        return NormalExcelConstants.EASYPOI_EXCEL_VIEW;//View名称


    }




}

四.前端验证(jquery.validate.js)
模板技术(freemarker-ftl,velocity-vm)

在这里插入图片描述

 save:function(){
            var id = $("#employeeId").val();
            var url="/employee/save";
            if(id){
                url="/employee/update?cmd=update";
            }
            //保存方法
            employeeForm.form('submit',{
                url:url,
                onSubmit: function(){
                    //重复密码验证 比较两个值是否相等 如果不相等 return false
                    //表示验证通过才能提交
                    return employeeForm.form("validate");
                },
                success:function(data){
                    var result = JSON.parse(data);//转成相应的json数据
                    if(result.success) {
                        employeeGrid.datagrid('reload');
                    }else{
                        $.messager.alert('提示信息','操作失败!,原因:'+result.msg,"error");
                    }
                    employeeDialog.dialog('close');
                }
            })
        }

模板技术

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);
    }

五.权限shrio(登录,菜单,访问) -> 理解
用户-角色-菜单/权限
身份验证
在这里插入图片描述

**
 * 自定义一个Realm
 */
public class MyRealm extends AuthorizingRealm {

    //获取到这个Realm的名称(随便取)
    @Override
    public String getName() {
        return "MyRealm";
    }

    //进行授权的认证
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }

    //进行登录的认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //明显的知道:这个authenticationToken就是UsernamePasswordtoken
        UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;
        String username = token.getUsername(); //拿到用户名(注:这个用户名是传过来的)
        //这里根据用户名去获取密码(如果没有获取到,相当于这个用户不存在,就返回null值)
        String password = getByName(username);
        if(password==null){
            return null;
        }
        //创建一个简单的身份信息(把用户名与密码放进去-注:它会自动的比较获取的密码与你传过来的密码)
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(username,password,getName());
        return authenticationInfo;
    }

//模拟从数据库中获取信息
    private String getByName(String username) {
        if("admin".equals(username)){
            return "123456";
        }else if("guest".equals(username)){
            return "abcd";
        }
        return null;
    }
}

授权管理

public class MyRealm extends AuthorizingRealm {

    //获取到这个Realm的名称(随便取)
    @Override
    public String getName() {
        return "MyRealm";
    }

    //进行授权的认证
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //拿到认证的主要信息(用户名)
        String username = (String) principalCollection.getPrimaryPrincipal();
        //模拟根据用户名拿到角色信息与权限信息
        Set<String> roles = getRolesByUsername(username);
        Set<String> permissions = getPermissionsByUsername(username);
        //拿到验证信息对象
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        //设置用户的角色
        authorizationInfo.setRoles(roles);
        //设置用户的权限
        authorizationInfo.setStringPermissions(permissions);
        return authorizationInfo;
    }

    //模拟根据用户名拿到角色的功能
    private Set<String> getRolesByUsername(String username) {
        Set<String> roles = new HashSet<>();
        roles.add("admin");
        roles.add("it");
        return roles;
    }
    //模拟根据用户名拿到权限的功能
    private Set<String> getPermissionsByUsername(String username) {
        Set<String> permissions = new HashSet<>();
        permissions.add("employee.*");
        permissions.add("department.save");
        return permissions;
    }
 ...
}

密码学(加密加盐)

@Test
     public void testMyRealm() throws Exception{
         //创建自己定义的Realm
         MyRealm myRealm = new MyRealm();
        //把Realm放到securityManager中去
         DefaultSecurityManager securityManager = new DefaultSecurityManager();
         securityManager.setRealm(myRealm);
         //把权限管理器放到相应的环境中(我们可以在项目任何位置拿到)
         SecurityUtils.setSecurityManager(securityManager);


         //设置咱们Realm的密码匹配器(我们的密码要怎么处理)
         HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
         matcher.setHashAlgorithmName("md5"); //匹配器使用MD5的算法
         matcher.setHashIterations(10);//加密算法要迭代多少次
         myRealm.setCredentialsMatcher(matcher);

         //拿到当前用户(Subject就是当前用户,游客)
         Subject currentUser = SecurityUtils.getSubject();
         //准备登录的令牌(准备用户名与密码) -> 这里的密码进行了加密
         UsernamePasswordToken token = new UsernamePasswordToken("admin","123456");

         try {
             //根据令牌进行功能登录(当前用户进行登录)
             currentUser.login(token);
             System.out.println("登录成功啦。。。。");
         } catch (UnknownAccountException e) {
             System.out.println("这个账号不存在!" + token.getPrincipal());
             e.printStackTrace();
         } catch (IncorrectCredentialsException ice) {
             System.out.println("这个密码不存在!" + token.getPrincipal());
             ice.printStackTrace();
         }catch (AuthenticationException e){
             System.out.println("i don't k");
         }
     }
/**
 * algorithmName:加密算法(md5,sha)
 *  source:原始密码
 *  salt,加盐
 *  hashIterations:遍历次数
 */
SimpleHash simpleHash = new SimpleHash("MD5","admin","itsource",10);
System.out.println(simpleHash);

会话管理

六.导入导出(上传,下载,Excel的创建与读取)
JXL:只支持Excel
POI: 支持多种办公软件
在这里插入图片描述在这里插入图片描述

**
 * 导出功能:下载
 * @param map
 * @return
 */
@RequestMapping("/download")
public String download(ModelMap map,EmployeeQuery query, HttpServletRequest request) {
    //拿到所有数据
    List<Employee> list = employeeService.findByQuery(query);
    //获取到真实路径
//解决了下载的图片的路径问题
    String realPath = request.getServletContext().getRealPath("");
    list.forEach(e -> {
        e.setHeadImage(realPath+e.getHeadImage());
        System.out.println(e.getHeadImage());
    });

    //设置一些属性
    ExportParams params = new ExportParams("员工管理", "明细", ExcelType.XSSF);
    //params.setFreezeCol(3);
    map.put(NormalExcelConstants.DATA_LIST, list); // 数据集合
    map.put(NormalExcelConstants.CLASS, Employee.class);//导出实体
    map.put(NormalExcelConstants.PARAMS, params);//参数
    map.put(NormalExcelConstants.FILE_NAME, "employee");//文件名称
    //返回的名称 :easypoiExcelView -> 并没有找我的bean,而且当做一个路径去进行访问
    //  现在默认去找的视图解析器,而没有找我的那一个bean
    return NormalExcelConstants.EASYPOI_EXCEL_VIEW;//View名称
}
 @RequestMapping("/employeeXlsx")
    public String employeeXlsx(MultipartFile empFile, HttpServletResponse response) throws Exception {
        //准备导入的参数
        ImportParams params = new ImportParams();
        params.setTitleRows(1);
        // need:需要  verfiy:核实,验证
        params.setNeedVerfiy(true); //需要验证

        //excel导入的一个结果
        ExcelImportResult<Employee> result = ExcelImportUtil.importExcelMore(
                empFile.getInputStream(),
                Employee.class, params);
        //引入正确的list(正常保存)
        result.getList()[验证完后正确的数据].forEach(e ->{
            // System.out.println(e+","+e.getDepartment());
            //根据部门名称拿到它的部门,再放到对应的员工中
            Department dept = departmentService.findByName(e.getDepartment().getName());
            e.setDepartment(dept);
            //给一个默认密码
            e.setPassword("123");
            employeeService.save(e);
        });
//        //引入错误的list
//        result.getFailList()[验证后错误的数据].forEach(e ->{
//            System.out.println("错误的:"+e);
//        });
        //如果有错误,就直接导出错误文件到前台
        // Verfiy:检验 Fail:失败
        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";
    }

七.产品模块(自己练习)
增删改查
在这里插入图片描述
八.采购模块(组合关系的操作)
日期的查询/组合关系的配置/(前端比较麻烦)
在这里插入图片描述

@Entity
@Table(name = "purchasebill")
public class Purchasebill extends BaseDomain {

    private Date vdate;// 交易时间 -> 需要录入(时间set的时候加上@DateTimeFormat(pattern = "yyyy-MM-dd"))
    private BigDecimal totalAmount; //总金额 -> 明细计算
    private BigDecimal totalNum; //总数量 -> 明细计算
    private Date inputTime = new Date(); //录入时间 ->系统自动生成 当前系统时间
    private Date auditorTime; //审核时间 -> 可以为空,审核时自己生成
    /**
     * 0待审,1已审,-1作废
     */
    private Integer status = 0; //单据状态 -> 默认待审

    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "supplier_id")
    private Supplier supplier;// 多对一,optional非空 供应商(需要选择)

    @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 = "buyer_id")
    private Employee buyer;// 多对一,非空 采购员 -> 需要

    // 一般组合关系使用List
    /**
     * 组合关系:整体和部分 不能分开 --强级联
     * 一个订单有多个明细
     * cascade:ALL 强级联(支持级联保存和级联删除)
     * mappedBy:让一方放弃管理 ,交给多方来维护 性能要高一点
     * 双向一对多或者多对一 一方放弃管理,性能要高一点
     * orphanRemoval:孤儿删除  可以通过一方删除多方  让一方解除关系 通过一方去上删除
     *
     */
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "bill", fetch = FetchType.LAZY, orphanRemoval = true)
    private List<Purchasebillitem> items = new ArrayList<Purchasebillitem>();

    @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;
    }

//产品明细
@Entity
@Table(name = "purchasebillitem")
public class Purchasebillitem 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")
    @JsonIgnore //生成json的时候忽略这个属性
    private Purchasebill bill;// 组合关系,非空

九.报表(HighCharts)
在这里插入图片描述在这里插入图片描述

ar itsource={
    //查询数据
    search:function () {
        //需要先引入 jquery.serializejson.js 才可以使用这个方法
        var params = searchForm.serializeJSON();
        purchaseBillItemGrid.datagrid('load',params);
    },
    chart3D:function () {
        var params = searchForm.serializeJSON();
        $.post("/purchaseBillItem/findCharts",params,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
                }]
            });
            purchaseBillItemDialog.dialog("center").dialog('open');
        })

    },
    chart2D:function () {
        var params = searchForm.serializeJSON();
        $.post("/purchaseBillItem/findCharts",params,function (result) {
            Highcharts.chart('purchaseBillItemDialog', {
                chart: {
                    plotBackgroundColor: null,
                    plotBorderWidth: null,
                    plotShadow: false,
                    type: 'pie'
                },
                title: {
                    text: '2018年1月浏览器市场份额'
                },
                tooltip: {
                    pointFormat: '{series.name}: <b>{point.percentage:.1f}%</b>'
                },
                plotOptions: {
                    pie: {
                        allowPointSelect: true,
                        cursor: 'pointer',
                        dataLabels: {
                            enabled: true,
                            format: '<b>{point.name}</b>: {point.percentage:.1f} %',
                            style: {
                                color: (Highcharts.theme && Highcharts.theme.contrastTextColor) || 'black'
                            }
                        }
                    }
                },
                series: [{
                    name: '比例',
                    colorByPoint: true,
                    data: result
                }]
            });
            purchaseBillItemDialog.dialog("center").dialog('open');
        })

    }
}

十.业务(定时调试,邮件发送)

在这里插入图片描述
定时任务


```javascript
@Service("QzJob")
public class QuartzJobServiceImpl implements IQuartzJobService {

    @Autowired
    private IProductstockService productstockService;

    @Override
    public void work() {
        System.out.println(productstockService);
        System.out.println("---------------------------------");
        String jpql ="select o from Productstock o where o.id = ?";
        List list = productstockService.findByJpql(jpql, 1L);
        if(list.size() > 0 ){
            System.out.println("库存不足");
        }else{
            System.out.println("库存很足...");
        }

    }
}

9.9.邮件

public class MailTest extends BaseServiceTest {
   @Autowired
   JavaMailSender mailSender;

   @Test
   public void testName() throws Exception {
//JavaMailSenderImpl xxx = (JavaMailSenderImpl)mailSender
      // 简单邮件对象
      SimpleMailMessage msg = new SimpleMailMessage();
      // 发送人:和配置一致
      msg.setFrom("zytestitsource1@126.com");
      // 收件人
      msg.setTo("zytestitsource@163.com");

      // 主题
      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("zytestitsource1@126.com");

      helper.setTo("zytestitsource@163.com");


      helper.setSubject("测试一把");
      helper.setText("<h1>男男女女</h1>",true);

      //发送附件
      helper.addAttachment("美女.jpg",new File("E:/timg.jpg"));
      helper.addAttachment("java.docx",new File("E:/新建文本文档 (2).txt"));
      mailSender.send(mimeMessage);
   }
}

3.项目模块部分
基础模块
用户 部门 权限 角色

业务模块(核心)
采购 (进货)管理 销售管理 库存管理

基础数据模块
数据字典

4.在项目中学习到的东西

4.1 熟悉了项目整体的搭建
4.2 对项目中的执行流程思路得到了加强
前端页面 — js easyui – controller—service—repository—query–domain
4.3 项目模块中涉及到了大量的增删改查,crud能力得到了提高
4.4 对项目中的业务有了基本的认识
4.5 学习到了一些新的技术 比如shrio easypoi highcharts 定时任务 邮件发送等…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值