1、库存预警报表
1.1、需求及实现思路
(1)需求:统计每种商品的库存数量和待发货数量(库存数量小于待发货数量)

(2)实现思路:运用关联查询、子查询、聚合统计。

1.2、SQL语句的编写
这种带子查询的 SQL 语句相对复杂,我们分步完成:
(1)按商品编号(和商品名称)分组统计各商品的库存合计
select g.uuid,g.name,sum(s.num) storenum
from goods g,storedetail s
where g.uuid=s.goodsuuid group by g.uuid,g.name;
结果如下:

但是这样的结果,只是显示库存表中存在的商品,而库存表中不存在的商品则不
会显示。我们可以通过左外连接的方式来进行查询。
select g.uuid,g.name,nvl(sum(s.num),0) storenum
from goods g,storedetail s
where g.uuid=s.goodsuuid(+) group by g.uuid,g.name;
我们采用 Oracle 的左外连接的语法符号 (+) 来实现左外连接
另外 nvl 函数用于将空值转换为特定的值
查询结果如下

(2)按商品编号分组统计各商品的待发货数量
select goodsuuid,sum(num) outnum
from orderdetail od,orders o
where od.ordersuuid=o.uuid
and od.state='0' and o.type='2' group by goodsuuid;
结果如下

(3)按商品查询库存数量与待发货数量(子查询)
select uuid,name,storenum,outnum from
(select g.uuid,g.name,nvl(sum(s.num),0) storenum
from goods g,storedetail s
where g.uuid=s.goodsuuid(+) group by g.uuid,g.name) gs,
(
select goodsuuid,sum(num) outnum
from orderdetail od,orders o
where od.ordersuuid=o.uuid
and od.state='0' and o.type='2' group by goodsuuid) odo
where gs.uuid=odo.goodsuuid
查询结果如下:

1.3、视图创建
我们上边的语句的查询结果是我们经常要用的,每次都写很繁琐,所以我们可以把常用的复杂查询直接建立为视图,这样可以极大简化我们的开发,便于日常维护。
create view view_storealert as
.....(SQL 语句)
建立完视图后,我们就可以直接使用这个视图了,如下图

1.4、代码书写
(1) 建立视图 t view_storealert 的实体类
package cn.itcast.erp.entity;
/**
* 库存警报
* @author Administrator
*
*/
public class StoreAlert {
private Long uuid;//商品编号
private String name;//商品名称
private Long storenum;//库存数量
private Long outnum;//待出库数量
public Long getUuid() {
return uuid;
}
public void setUuid(Long uuid) {
this.uuid = uuid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Long getStorenum() {
return storenum;
}
public void setStorenum(Long storenum) {
this.storenum = storenum;
}
public Long getOutnum() {
return outnum;
}
public void setOutnum(Long outnum) {
this.outnum = outnum;
}
}
(2) 编写视图 t view_storealert
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD
3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="cn.itcast.erp.entity.StoreAlert"
table="view_storealert" >
<id name="uuid" >
</id>
<property name="name" />
<property name="storenum" />
<property name="outnum" />
</class>
</hibernate-mapping>
(3)编写数据访问层代码
在 StoredetailDao 和 IStoredetailDao 新增方法查询库存预警列表

(4)编写业务逻辑层代码
在 StoredetailBiz 和 IStoredetailBiz 新增方法查询库存预警列表

(5)编写Action层代码
在 StoredetailAction 新增方法查询库存预警列表

(6)编写页面代码
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>库存预警报表</title>
<link rel="stylesheet" type="text/css"
href="ui/themes/default/easyui.css">
<link rel="stylesheet" type="text/css" href="ui/themes/icon.css">
<script type="text/javascript" src="ui/jquery.min.js"></script>
<script type="text/javascript" src="ui/jquery.easyui.min.js"></script>
<script type="text/javascript"
src="ui/locale/easyui-lang-zh_CN.js"></script>
<script type="text/javascript" src="js/storealert.js"></script>
</head>
<body>
<table id="grid"></table>
</body>
</html>
(7)编写storealert.js代码
$(function(){
$('#grid').datagrid({
url:'storedetail_storeAlertList.action',
columns:[[
{field:'uuid',title:'商品编号',width:100},
{field:'name',title:'商品名称',width:100},
{field:'storenum',title:'库存数量',width:100},
{field:'outnum',title:'待发货数量',width:100}
]]
});
})
2、JavaMail 实现发送预警邮件(扩展)
2.1、需求分析
在上例基础上,实现发送邮件预警功能 。点击“发送警报邮件”按钮即可发送库存报警。

如果存在报警商品并成功发送,提示发送成功
如果不存在报警商品,提示没有库存报警商品
2.2、什么是JavaMail
JavaMail是提供给开发人员在应用程序中实现邮件发送和接收功能而提供的一套标准开发类库,支持常用的邮件协议,如 SMTP、POP3、IMAP,开发人员使用 JavaMail 编写邮件程序时,无需考虑底层的通信细节(Socket),JavaMail 也提供了能够创建出各种复杂 MIME 格式的邮件内容的 API。使用 JavaMail,我们可以实现类似 OutLook、FoxMail 的软件。
Spring JavaMailSender 是对 JavaMail 进行封装,简化了开发,告别繁琐的 API .
2.3、入门小Demo
(1)申请邮箱
我这里申请的是新浪邮箱,进入后开通 POP3/SMTP 服务

(2)创建工程,编写pom文件
创建 demo 工程,pom.xml 中添加 spring 和 javaMail 的依赖,下面是 javaMail 的依赖
<!-- Javamail -->
<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
<version>1.4.4</version>
</dependency>
(3)编写用于发送Email的类
package cn.itcast.demo;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
public class TestSend {
private JavaMailSender javaMailSender;
public void setJavaMailSender(JavaMailSender javaMailSender) {
this.javaMailSender = javaMailSender;
}
public void sendMail() throws MessagingException{
MimeMessage mime = javaMailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mime);
helper.setFrom("wwwitcastcn@sina.com");//发件人
helper.setTo("sunwukong@sina.com");//收件人
helper.setSubject("测试邮件");//主题
helper.setText("你要是收到这封邮件,你就成功了,嘿嘿~~~~");//内容
javaMailSender.send(mime);
}
}
(4) 编写spring配置文件applicationContext_mail.xml
<bean id="mailSender"
class="org.springframework.mail.javamail.JavaMailSenderImpl">
<property name="protocol" value="smtp"/>
<property name="host" value="smtp.sina.com"/>
<property name="port" value="25" />
<property name="username" value="wwwitcastcn@sina.com"/>
<property name="password" value="itcast27"/>
<property name="javaMailProperties">
<props>
<prop key="mail.smtp.auth">true</prop>
</props>
</property>
</bean>
<bean id="testSend" class="cn.itcast.demo.TestSend">
<property name="javaMailSender" ref="mailSender"></property>
</bean>
(5)编写测试代码
ApplicationContext context=new
ClassPathXmlApplicationContext("applicationContext_mail.xml");
TestSend testSend= (TestSend) context.getBean("testSend");
testSend.sendMail();
2.4、ERP实现发送库存预警邮件
(1)在pom中添加依赖
<!-- Javamail -->
<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
<version>1.4.4</version>
</dependency>
(2)编写发送邮件的工具类
package util;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
/**
* 邮件工具类
* @author Administrator
*
*/
public class MailUtil{
private JavaMailSender javaMailSender;
public void setJavaMailSender(JavaMailSender javaMailSender) {
this.javaMailSender = javaMailSender;
}
private String fromAddress;
public void setFromAddress(String fromAddress) {
this.fromAddress = fromAddress;
}
/**
* 发送邮件
* @param toAddress
* @param subject
* @param text
* @throws MessagingException
*/
public void sendMail(String toAddress,String subject,String text)
throws MessagingException{
MimeMessage mime = javaMailSender.createMimeMessage();
MimeMessageHelper helper=new MimeMessageHelper(mime);
helper.setSubject(subject);
helper.setFrom(fromAddress);
helper.setTo(toAddress);
helper.setText(text);
javaMailSender.send(mime);
}
}
(3)编写spring配置文件
<bean id="mailSender"
class="org.springframework.mail.javamail.JavaMailSenderImpl">
<property name="protocol" value="smtp"/>
<property name="host" value="smtp.sina.com"/>
<property name="port" value="25" />
<property name="username" value="wwwitcastcn@sina.com"/>
<property name="password" value="itcast"/>
<property name="javaMailProperties">
<props>
<prop key="mail.smtp.auth">true</prop>
</props>
</property>
</bean>
<!-- Mail 工具类 -->
<bean id="mailUtil" class="util.MailUtil">
<property name="javaMailSender" ref="mailSender"></property>
<property name="fromAddress"
value="wwwitcastcn@sina.com"></property>
</bean>
(4)编写业务逻辑类
在 StoredetailBiz 类和 IStoredetailBiz 接口新增方法
private String toAddress;//收件箱
private String subject;//主题
private String text;//正文
public void setToAddress(String toAddress) {
this.toAddress = toAddress;
}
public void setSubject(String subject) {
this.subject = subject;
}
public void setText(String text) {
this.text = text;
}
/**
* 发送库存预警邮件
* @throws MessagingException
*/
public void sendStoreAlertMail() throws MessagingException{
//得到库存预警列表
List<StoreAlert> storeAlertList =
storedetailDao.getStoreAlertList();
if(storeAlertList.size()>0){
mailUtil.sendMail( toAddress,
subject.replace("[time]", new SimpleDateFormat("yyyy-MM-dd
hh:mm:ss").format(new Date())),
text.replace("[count]", String.valueOf(storeAlertList.size()) ));
}else{
throw new ErpException("没有库存预警信息");
}
}
(5)修改配置文件applicationContext-biz.xml
<!-- 仓库库存 -->
<bean id="storedetailBiz"
class="cn.itcast.erp.biz.impl.StoredetailBiz">
<property name="storedetailDao" ref="storedetailDao"></property>
<!-- 邮件工具 -->
<property name="mailUtil" ref="mailUtil"></property>
<!-- 收件人 -->
<property name="toAddress" value=" 收件人邮箱 @sina.com"></property>
<!-- 邮件主题 -->
<property name="subject" value=" 库存预警邮件 -- 时间:
[time]"></property>
<!-- 邮件内容 -->
<property name="text" value=" 亲,目前有 [count] 种商品出现库存预警,请
登陆系统查询 "></property>
</bean>
(6)编写Action代码
在 StoredetailAction 类新增方法
/**
* 发送警报邮件
*/
public void sendAlertMail(){
try {
storedetailBiz.sendAlertMail();
write(ajaxReturn(true, "发送成功"));
} catch (MessagingException e) {
write(ajaxReturn(false, "无法发送邮件"));
e.printStackTrace();
}catch(ErpException e){
write(ajaxReturn(false,e.getMessage()));
}
}
(7)编写JS代码
修改 storealert.js, 为 datagrid 添加 toolbar 属性
toolbar: [{
iconCls: 'icon-add',
text:'发送预警邮件',
handler: function(){
$.ajax({
url:'storedetail_sendStoreAlertMail.action',
dataType:'json',
success:function(value){
$.messager.alert('提示',value.message);
}
});
}
}]
点击按钮,测试运行。打开邮箱,查看是否可以收到发送过来的邮件。
3、Quartz 实现定时发送预警邮件(重点)
3.1、需求及实现思路
定时查询库存预警信息,如果存在库存预警信息,发送邮件通知给相关工作人员。
3.2、什么是 Quartz 框架
Quartz 是一个开源的作业调度框架,它完全由 Java 写成,并设计用于 J2SE 和 J2EE 应用中。它提供了巨大的灵活性而不牺牲简单性。你能够用它来为执行一个作业而创建简单的或复杂的调度。
(1)Job
表示一个任务(工作),要执行的具体内容。
(2)JobDetail
JobDetail 表示一个具体的可执行的调度程序,Job 是这个可执行程调度程序所要执行的内容,另外 JobDetail 还包含了这个任务调度的方案和策略。
(3)Trigger 代表一个调度参数的配置,什么时候去调。
(4)Scheduler 代表一个调度容器,一个调度容器中可以注册多个 JobDetail 和Trigger。当 Trigger 与 JobDetail 组合,就可以被 Scheduler 容器调度了。
3.3、入门小Demo
需求,每间隔 10 秒,控制台打印输出“调度程序已执行”+时间
(1)创建工程,引入Quartz框架
pom.xml 中添加 spring 的依赖 和 quartz 的依赖 和相关插件
spring 的依赖和相关插件参照上边的工程 ,quartz 的依赖如下
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.2.3</version>
</dependency>
(2)编写任务类
package cn.itcast.demo;
import java.util.Date;
/**
* 工作类
* @author Administrator
*
*/
public class JobTest {
public void mylogic(){
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
System.out.println("调度程序已执行"+sdf.format(new Date()));
}
}
(3)编写配置文件
添加配置文件 applicationContext_job.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"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 定义一个任务类 -->
<bean id="job" class="cn.itcast.demo.JobTest">
</bean>
<!-- 任务类描述 -->
<bean id="jobDetail"
class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFac
toryBean">
<property name="targetObject" ref="job"></property>
<property name="targetMethod" value="mylogic"></property>
</bean>
<!-- 触发器 -->
<bean id="mailTrigger"
class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="jobDetail" ref="jobDetail"></property>
<!-- cron 表达式 每 10 秒执行一次-->
<property name="cronExpression" value="0/10 * * * * ?"></property>
</bean>
<!-- 总管理容器 -->
<bean id="startQuartz"
class="org.springframework.scheduling.quartz.SchedulerFactoryBean" >
<property name="triggers">
<list>
<ref bean="mailTrigger"/>
</list>
</property>
</bean>
</beans>
(4)编写测试类启动spring容器
ApplicationContext context=new
ClassPathXmlApplicationContext("applicationContext_job.xml");
3.4、Crom表达式详解
Cron 表达式是一个字符串,字符串以 5 或 6 个空格隔开,分为 6 或 7 个域,每一个域代表一个含义。
1. 域
Seconds(秒):可出现", - * /"四个字符,有效范围为 0-59 的整数
Minutes(分钟):可出现", - * /"四个字符,有效范围为 0-59 的整数
Hours(小时):可出现", - * /"四个字符,有效范围为 0-23 的整数
DayofMonth(日 of 月):可出现", - * / ? LW C"八个字符,有效范围为 1-31 的整
数
Month(月):可出现", - * /"四个字符,有效范围为 1-12 的整数
DayofWeek(日 of 星期):可出现", - * / ? L C #"八个字符,有效范围为 1-7 的整
数
1 表示星期天,2 表示星期一, 依次类推
Year(年):可出现", - * /"四个字符,有效范围为 1970-2099 年
. 2. 字符含义
(1) * 表示匹配该域的任意值,假如在 Minutes 域使用*, 即表示每分钟都会触发
事件。
(2) ? 表示不指定值。只能用在 DayofMonth 和 DayofWeek 两个域。因为DayofMonth 和 DayofWeek 会相互影响。例如想在每月的 20 日触发调度,不管20 日到底是星期几,则只能使用如下写法: 13 13 15 20 * ?, 其中最后一位只能用?,而不能使用*,如果使用*表示不管星期几都会触发。
(3) - 表示范围,例如在 Minutes 域使用 5-20,表示从 5 分到 20 分钟每分钟触发
一次
(4) / 表示起始时间开始触发,然后每隔固定时间触发一次,例如在 Minutes 域使用 5/20,则意味着 5 分钟触发一次,而 25,45 等分别触发一次.
(5) , 表示列出枚举值值。例如:在 Minutes 域使用 5,20,则意味着在 5 和 20 分每分钟触发一次。
(6)L 表示最后,只能出现在 DayofMonth 和 DayofWeek 域。如果在 DayofMonth写 L 表示这个月的最后一天,如果在 DayofWeek 写 L 表示每个星期的最后一天(星期六) 。如果在 DayofWeek 域使用 5L,意味着在最后的一个星期四触发。
(7)W 表示最近有效工作日(周一到周五),只能出现在 DayofMonth 域,系统将在离指定日期的最近的有效工作日触发事件。例如:在 DayofMonth 使用 5W,如果 5 日是星期六,则将在最近的工作日:星期五,即 4 日触发。如果 5 日是星期
天,则在 6 日(周一)触发;如果 5 日在星期一到星期五中的一天,则就在 5 日触发。另外一点,W 的最近寻找不会跨过月份
(8)LW:这两个字符可以连用,表示在某个月最后一个工作日,即最后一个星期五。
(9)#:用于确定每个月第几个星期几,只能出现在 DayofWeek 域。例如在 4#2,表示某月的第二个星期三。
3、示例
"0 0 12 * * ?" 每天中午 12 点触发
"0 15 10 ? * *" 每天上午 10:15 触发
"0 15 10 * * ?" 每天上午 10:15 触发
"0 15 10 * * ? *" 每天上午 10:15 触发
"0 15 10 * * ? 2005" 2005 年的每天上午 10:15 触发
"0 * 14 * * ?" 在每天下午 2 点到下午 2:59 期间的每 1 分钟触发
"0 0/5 14 * * ?" 在每天下午 2 点到下午 2:55 期间的每 5 分钟触发
"0 0/5 14,18 * * ?" 在每天下午 2 点到 2:55 期间和下午 6 点到 6:55 期间的每 5 分
钟触发
"0 0-5 14 * * ?" 在每天下午 2 点到下午 2:05 期间的每 1 分钟触发
"0 10,44 14 ? 3 4" 每年三月的星期三的下午 2:10 和 2:44 触发
"0 15 10 ? * MON-FRI" 周一至周五的上午 10:15 触发
"0 15 10 15 * ?" 每月 15 日上午 10:15 触发
"0 15 10 L * ?" 每月最后一日的上午 10:15 触发
"0 15 10 ? * 6L" 每月的最后一个星期五上午 10:15 触发
"0 15 10 ? * 6L 2002-2005" 2002 年至 2005 年的每月的最后一个星期五上午 10:15
触发
"0 15 10 ? * 6#3" 每月的第三个星期五上午 10:15 触发
3.5、代码实现
(1)修改pom.xml文件,添加Quartz依赖
<!-- 调度框架 Quartz -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.2.3</version>
</dependency>
(2)创建任务类,执行发送警报邮件的方法
package cn.itcast.erp.job;
import javax.mail.MessagingException;
import cn.itcast.erp.biz.IStoredetailBiz;
/**
* 邮件相关任务
* @author Administrator
*
*/
public class MailJob {
private IStoredetailBiz storedetailBiz;
public void setStoredetailBiz(IStoredetailBiz storedetailBiz) {
this.storedetailBiz = storedetailBiz;
}
/**
* 发送库存报警邮件
*/
public void sendStoreAlertMail(){
try {
storedetailBiz.sendStoreAlertMail();
} catch (MessagingException e) {
e.printStackTrace();
}
}
}
(3)编写配置文件applicationContext-job.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"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 发送邮件任务类 -->
<bean id="mailJob" class="cn.itcast.erp.job.MailJob">
<property name="storedetailBiz" ref="storedetailBiz"></property>
</bean>
<!-- 发送库存报警邮件任务-->
<bean id="sendStoreAlertMailJobDetail"
class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFac
toryBean">
<property name="targetObject" ref="mailJob"></property>
<property name="targetMethod"
value="sendStoreAlertMail"></property>
</bean>
<!-- 触发器 -->
<bean id="mailTrigger"
class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="jobDetail"
ref="sendStoreAlertMailJobDetail"></property>
<!-- cron 表达式 (时间点的描述) 每天的 上午 12:20 和下午 16:20 的时间
执行 -->
<property name="cronExpression" value="0 17 12,16 * * ?"></property>
</bean>
<!-- 总管理容器 -->
<bean id="startQuartz"
class="org.springframework.scheduling.quartz.SchedulerFactoryBean" >
<property name="triggers">
<list>
<ref bean="mailTrigger"/>
</list>
</property>
</bean>
</beans>
启动运行

4、知识点总结
4.1、SQL 查询:关联、子查询、聚合统计函数
4.2、视图:简化开发。(编写实体类和映射文件与表相同)
4.3、JavaMail :发送邮件的 API .Spring 封装的 JavaMailSender 简化了开发
4.4、Quartz : 系统调度框架。 定时执行任务。Cron 表达式。