我的电脑操作系统版本为Win7旗舰版(ServicePack1),Oracle版本为Oracle11g
程序使用的jar包有:mybatis-3.2.2.jar、ojdbc14-10.2.0.2.0.jar
本例中使用的配置文件mybatis-config.xml,可以参见我的另一篇Blog《一个简单的MyBatis连接Oracle数据库的例子》(http://my.oschina.net/Tsybius2014/blog/626206)
本文还是一个使用MyBatis进行查询的练习之作。
现有三张表:
1)OPERATOR表(签到人员信息表)记录了人员的ID和名称,该表中每个打卡人都作为一条数据存入
2)WORKDAY表(工作日历表)记录了工作日历,该表中每日都作为一条数据存入,INFO_DATE列为日期,IS_WORK_DAY列为1时为工作日,为0时为非工作日
3)CLOCKING_IN_DATA表(打卡数据表)记录了考勤打卡情况,每次打卡向该表中插入一条数据,OPER_DATE为打卡日期,OPER_TIME为打卡时间,OPER_ID为打卡人ID,OPER_NAME为打卡人姓名
要求根据这三张表,计算出指定人的出勤情况。
使用下面的SQL脚本,可以向Oracle数据库中插入基础数据:
-- 签到人员信息表
CREATE TABLE OPERATOR
(
ID NUMBER(12, 0) PRIMARY KEY,
OPER_ID VARCHAR(10),
OPER_NAME VARCHAR(10)
);
INSERT INTO OPERATOR (ID, OPER_ID, OPER_NAME) VALUES (1, '0001', 'Tsybius');
INSERT INTO OPERATOR (ID, OPER_ID, OPER_NAME) VALUES (2, '0002', 'Galatea');
-- 工作日历表
CREATE TABLE WORKDAY
(
ID NUMBER(12, 0) PRIMARY KEY,
INFO_DATE NUMBER(10, 0),
IS_WORK_DAY CHAR
);
-- 2016年2月1-9日的工作日(1-5、8、9是工作日,6、7不是工作日)
INSERT INTO WORKDAY (ID, INFO_DATE, IS_WORK_DAY) VALUES (1, 20160201, '1');
INSERT INTO WORKDAY (ID, INFO_DATE, IS_WORK_DAY) VALUES (2, 20160202, '1');
INSERT INTO WORKDAY (ID, INFO_DATE, IS_WORK_DAY) VALUES (3, 20160203, '1');
INSERT INTO WORKDAY (ID, INFO_DATE, IS_WORK_DAY) VALUES (4, 20160204, '1');
INSERT INTO WORKDAY (ID, INFO_DATE, IS_WORK_DAY) VALUES (5, 20160205, '1');
INSERT INTO WORKDAY (ID, INFO_DATE, IS_WORK_DAY) VALUES (6, 20160206, '0');
INSERT INTO WORKDAY (ID, INFO_DATE, IS_WORK_DAY) VALUES (7, 20160207, '0');
INSERT INTO WORKDAY (ID, INFO_DATE, IS_WORK_DAY) VALUES (8, 20160208, '1');
INSERT INTO WORKDAY (ID, INFO_DATE, IS_WORK_DAY) VALUES (9, 20160209, '1');
-- 打卡数据表
CREATE TABLE CLOCKING_IN_DATA
(
ID NUMBER(12, 0) PRIMARY KEY,
OPER_DATE NUMBER(10, 0),
OPER_TIME NUMBER(10, 0),
OPER_ID VARCHAR(10),
OPER_NAME VARCHAR(10)
);
-- Tsybius 的上下班数据 20160201-20160207
-- 正常上下班1 20160201
INSERT INTO CLOCKING_IN_DATA (ID, OPER_DATE, OPER_TIME, OPER_ID, OPER_NAME) VALUES (1, 20160201, 85531, '0001', 'Tsybius');
INSERT INTO CLOCKING_IN_DATA (ID, OPER_DATE, OPER_TIME, OPER_ID, OPER_NAME) VALUES (2, 20160201, 180103, '0001', 'Tsybius');
-- 正常上下班2 20160202
INSERT INTO CLOCKING_IN_DATA (ID, OPER_DATE, OPER_TIME, OPER_ID, OPER_NAME) VALUES (3, 20160202, 85814, '0001', 'Tsybius');
INSERT INTO CLOCKING_IN_DATA (ID, OPER_DATE, OPER_TIME, OPER_ID, OPER_NAME) VALUES (4, 20160202, 180809, '0001', 'Tsybius');
-- 迟到 20160203
INSERT INTO CLOCKING_IN_DATA (ID, OPER_DATE, OPER_TIME, OPER_ID, OPER_NAME) VALUES (5, 20160203, 90302, '0001', 'Tsybius');
INSERT INTO CLOCKING_IN_DATA (ID, OPER_DATE, OPER_TIME, OPER_ID, OPER_NAME) VALUES (6, 20160203, 180809, '0001', 'Tsybius');
-- 早退 20160204
INSERT INTO CLOCKING_IN_DATA (ID, OPER_DATE, OPER_TIME, OPER_ID, OPER_NAME) VALUES (7, 20160204, 84513, '0001', 'Tsybius');
INSERT INTO CLOCKING_IN_DATA (ID, OPER_DATE, OPER_TIME, OPER_ID, OPER_NAME) VALUES (8, 20160204, 155324, '0001', 'Tsybius');
-- 迟到 + 早退 20160205
INSERT INTO CLOCKING_IN_DATA (ID, OPER_DATE, OPER_TIME, OPER_ID, OPER_NAME) VALUES (9, 20160205, 103501, '0001', 'Tsybius');
INSERT INTO CLOCKING_IN_DATA (ID, OPER_DATE, OPER_TIME, OPER_ID, OPER_NAME) VALUES (10, 20160205, 175000, '0001', 'Tsybius');
-- 旷工 - 只打了一次卡 20160208
INSERT INTO CLOCKING_IN_DATA (ID, OPER_DATE, OPER_TIME, OPER_ID, OPER_NAME) VALUES (11, 20160208, 83902, '0001', 'Tsybius');
-- 旷工 - 一次卡都没打 20160209
-- Galatea 的上下班数据 20160201-20160207
-- 加班到晚上11点 20160201
INSERT INTO CLOCKING_IN_DATA (ID, OPER_DATE, OPER_TIME, OPER_ID, OPER_NAME) VALUES (12, 20160201, 85531, '0002', 'Galatea');
INSERT INTO CLOCKING_IN_DATA (ID, OPER_DATE, OPER_TIME, OPER_ID, OPER_NAME) VALUES (13, 20160201, 235344, '0002', 'Galatea');
-- 加班到午夜12点,但12点前没有打卡 20160202
INSERT INTO CLOCKING_IN_DATA (ID, OPER_DATE, OPER_TIME, OPER_ID, OPER_NAME) VALUES (14, 20160202, 85349, '0002', 'Galatea');
INSERT INTO CLOCKING_IN_DATA (ID, OPER_DATE, OPER_TIME, OPER_ID, OPER_NAME) VALUES (15, 20160203, 3023, '0002', 'Galatea');
-- 正常上下班1 20160203
INSERT INTO CLOCKING_IN_DATA (ID, OPER_DATE, OPER_TIME, OPER_ID, OPER_NAME) VALUES (16, 20160203, 85958, '0002', 'Galatea');
INSERT INTO CLOCKING_IN_DATA (ID, OPER_DATE, OPER_TIME, OPER_ID, OPER_NAME) VALUES (17, 20160203, 180004, '0002', 'Galatea');
-- 系统升级一早就要去看着 20160204
INSERT INTO CLOCKING_IN_DATA (ID, OPER_DATE, OPER_TIME, OPER_ID, OPER_NAME) VALUES (18, 20160204, 53824, '0002', 'Galatea');
INSERT INTO CLOCKING_IN_DATA (ID, OPER_DATE, OPER_TIME, OPER_ID, OPER_NAME) VALUES (19, 20160204, 180213, '0002', 'Galatea');
-- 正常上下班2 20160205
INSERT INTO CLOCKING_IN_DATA (ID, OPER_DATE, OPER_TIME, OPER_ID, OPER_NAME) VALUES (20, 20160205, 84509, '0002', 'Galatea');
INSERT INTO CLOCKING_IN_DATA (ID, OPER_DATE, OPER_TIME, OPER_ID, OPER_NAME) VALUES (21, 20160205, 180329, '0002', 'Galatea');
-- 正常上下班3 20160208
INSERT INTO CLOCKING_IN_DATA (ID, OPER_DATE, OPER_TIME, OPER_ID, OPER_NAME) VALUES (22, 20160208, 85944, '0002', 'Galatea');
INSERT INTO CLOCKING_IN_DATA (ID, OPER_DATE, OPER_TIME, OPER_ID, OPER_NAME) VALUES (23, 20160208, 180114, '0002', 'Galatea');
-- 最后一天加班到半夜12点,12点前打了一次卡 20160209
INSERT INTO CLOCKING_IN_DATA (ID, OPER_DATE, OPER_TIME, OPER_ID, OPER_NAME) VALUES (24, 20160209, 84509, '0002', 'Galatea');
INSERT INTO CLOCKING_IN_DATA (ID, OPER_DATE, OPER_TIME, OPER_ID, OPER_NAME) VALUES (25, 20160209, 235603, '0002', 'Galatea');
INSERT INTO CLOCKING_IN_DATA (ID, OPER_DATE, OPER_TIME, OPER_ID, OPER_NAME) VALUES (26, 20160210, 2003, '0002', 'Galatea');
COMMIT;
/
为统计出指定人的考勤打卡情况,我写了一个SQL,用于查询编号为0001的人(Tsybius)的出勤记录
统计规则如下:
1、一日的时间从当日0点起,至当日235959点止,每日统计最早的一次和最晚的一次打卡记录
2、第一次打卡事件晚于9点钟判定为迟到,最后一次打卡时间早于18点钟算早退
3、一天打卡次数少于2次、不少于2次但2次都在12点前或12点后,按旷工计算
其中条件3在实际查询中可以转换为:不打卡按旷工计算,仅上午或仅下午有打卡记录按旷工计算
查询结果约定如下:0为正常考勤,1为迟到,2为早退,3为迟到+早退,4为旷工
SELECT A.INFO_DATE,
A.IS_WORK_DAY,
C.OPER_ID,
C.OPER_NAME,
B.MIN_TIME,
B.MAX_TIME,
NVL(B.ATTENDANCE, 4) AS ATT_STATUS
FROM (SELECT INFO_DATE, IS_WORK_DAY
FROM WORKDAY) A
LEFT JOIN (SELECT CI_DATA.*,
(CASE
WHEN CI_DATA.MIN_TIME < 90000 AND
CI_DATA.MAX_TIME > 180000 THEN
'0'
ELSE
CASE
WHEN CI_DATA.MIN_TIME > 120000 OR
CI_DATA.MAX_TIME < 120000 THEN
'4'
ELSE
CASE
WHEN CI_DATA.MIN_TIME > 90000 AND
CI_DATA.MAX_TIME > 180000 THEN
'1'
ELSE
CASE
WHEN CI_DATA.MIN_TIME < 90000 AND
CI_DATA.MAX_TIME < 180000 THEN
'2'
ELSE
'3'
END END END END) AS ATTENDANCE
FROM (SELECT OPER_DATE,
OPER_ID,
OPER_NAME,
MIN(OPER_TIME) AS MIN_TIME,
MAX(OPER_TIME) AS MAX_TIME
FROM CLOCKING_IN_DATA
WHERE OPER_ID = '0001'
GROUP BY OPER_DATE, OPER_ID, OPER_NAME) CI_DATA) B
ON A.INFO_DATE = B.OPER_DATE,
(SELECT OPER_ID, OPER_NAME
FROM OPERATOR
WHERE OPER_ID = '0001') C
ORDER BY INFO_DATE
在PL/SQL中执行这个SQL语句,查询出的结果集如下:
现使用MyBatis查询这些数据,Java代码如下:
import java.io.InputStream;
import java.text.MessageFormat;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
/**
* MyBatis使用测试
* @author Tsybius2014
* @date 2016年3月20日
* @time 下午5:40:55
* @remark
*
*/
public class MyBatisTest {
public static void main(String[] args) {
try {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = sqlSessionFactory.openSession();
try {
ClockingInMapper mapper = session.getMapper(ClockingInMapper.class);
//按operId获取考勤记录
printAttrStatus(mapper.selectAttStatusByOperId("0001"));
printAttrStatus(mapper.selectAttStatusByOperId("0002"));
} finally {
session.close();
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
/**
* 输出考勤记录
* @param attrStatusList 考勤记录表
*/
public static void printAttrStatus(List<Map<String, Object>> attrStatusList) {
if (attrStatusList == null || attrStatusList.size() == 0) {
return;
}
System.out.println("******** 考勤记录 START ********");
Iterator<Map<String, Object>> iter = attrStatusList.iterator();
while (iter.hasNext()) {
Map<String, Object> attrStatus = iter.next();
String infoDate = attrStatus.get("infoDate").toString();
String isWorkDay = attrStatus.get("isWorkDay").toString();
String operId = attrStatus.get("operId").toString();
String operName = attrStatus.get("operName").toString();
String minTime = attrStatus.get("minTime") != null ? attrStatus.get("minTime").toString() : "";
String maxTime = attrStatus.get("maxTime") != null ? attrStatus.get("maxTime").toString() : "";
String attStatus = attrStatus.get("attStatus").toString();
String attResult = "";
if (isWorkDay.equals("0")) {
attResult = "非工作日不考勤";
} else if (isWorkDay.equals("1")) {
if (attStatus.equals("0")) {
attResult = "考勤正常";
} else if (attStatus.equals("1")) {
attResult = "迟到";
} else if (attStatus.equals("2")) {
attResult = "早退";
} else if (attStatus.equals("3")) {
attResult = "迟到,早退";
} else if (attStatus.equals("4")) {
attResult = "旷工";
} else {
attResult = "非法数据";
}
} else {
attResult = "非法数据";
}
String result = MessageFormat.format(
"日期: {0},人员:{1}({2}),考勤结果:{3},{4}-{5}",
infoDate, operName, operId, attResult, minTime, maxTime);
System.out.println(result);
}
System.out.println("******** 考勤记录 END ********");
}
}
ClockingInMapper.java代码如下:
import java.util.List;
import java.util.Map;
public interface ClockingInMapper {
List<Map<String, Object>> selectAttStatusByOperId(String operId);
}
ClockingInMapper.xml代码如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="ClockingInMapper">
<resultMap id="AttendanceMap" type="java.util.Map" >
<result column="INFO_DATE" property="infoDate" jdbcType="DECIMAL" />
<result column="IS_WORK_DAY" property="isWorkDay" jdbcType="CHAR" />
<result column="OPER_ID" property="operId" jdbcType="VARCHAR" />
<result column="OPER_NAME" property="operName" jdbcType="VARCHAR" />
<result column="MIN_TIME" property="minTime" jdbcType="DECIMAL" />
<result column="MAX_TIME" property="maxTime" jdbcType="DECIMAL" />
<result column="ATT_STATUS" property="attStatus" jdbcType="CHAR" />
</resultMap>
<select id="selectAttStatusByOperId"
parameterType="java.lang.String" resultMap="AttendanceMap" >
SELECT A.INFO_DATE,
A.IS_WORK_DAY,
C.OPER_ID,
C.OPER_NAME,
B.MIN_TIME,
B.MAX_TIME,
NVL(B.ATTENDANCE, '4') AS ATT_STATUS
FROM (SELECT INFO_DATE, IS_WORK_DAY
FROM WORKDAY) A
LEFT JOIN (SELECT CI_DATA.*,
(CASE
WHEN CI_DATA.MIN_TIME <![CDATA[<]]> 90000 AND
CI_DATA.MAX_TIME <![CDATA[>]]> 180000 THEN
'0'
ELSE
CASE
WHEN CI_DATA.MIN_TIME <![CDATA[>]]> 120000 OR
CI_DATA.MAX_TIME <![CDATA[<]]> 120000 THEN
'4'
ELSE
CASE
WHEN CI_DATA.MIN_TIME <![CDATA[>]]> 90000 AND
CI_DATA.MAX_TIME <![CDATA[>]]> 180000 THEN
'1'
ELSE
CASE
WHEN CI_DATA.MIN_TIME <![CDATA[<]]> 90000 AND
CI_DATA.MAX_TIME <![CDATA[<]]> 180000 THEN
'2'
ELSE
'3'
END END END END) AS ATTENDANCE
FROM (SELECT OPER_DATE,
OPER_ID,
OPER_NAME,
MIN(OPER_TIME) AS MIN_TIME,
MAX(OPER_TIME) AS MAX_TIME
FROM CLOCKING_IN_DATA
WHERE OPER_ID = #{operId, jdbcType = VARCHAR}
GROUP BY OPER_DATE, OPER_ID, OPER_NAME) CI_DATA) B
ON A.INFO_DATE = B.OPER_DATE,
(SELECT OPER_ID, OPER_NAME
FROM OPERATOR
WHERE OPER_ID = #{operId, jdbcType = VARCHAR}) C
ORDER BY INFO_DATE
</select>
</mapper>
这段Java代码的运行结果如下:
******** 考勤记录 START ********
日期: 20160201,人员:Tsybius(0001),考勤结果:考勤正常,85531-180103
日期: 20160202,人员:Tsybius(0001),考勤结果:考勤正常,85814-180809
日期: 20160203,人员:Tsybius(0001),考勤结果:迟到,90302-180809
日期: 20160204,人员:Tsybius(0001),考勤结果:早退,84513-155324
日期: 20160205,人员:Tsybius(0001),考勤结果:迟到,早退,103501-175000
日期: 20160206,人员:Tsybius(0001),考勤结果:非工作日不考勤,-
日期: 20160207,人员:Tsybius(0001),考勤结果:非工作日不考勤,-
日期: 20160208,人员:Tsybius(0001),考勤结果:旷工,83902-83902
日期: 20160209,人员:Tsybius(0001),考勤结果:旷工,-
******** 考勤记录 END ********
******** 考勤记录 START ********
日期: 20160201,人员:Galatea(0002),考勤结果:考勤正常,85531-235344
日期: 20160202,人员:Galatea(0002),考勤结果:旷工,85349-85349
日期: 20160203,人员:Galatea(0002),考勤结果:考勤正常,3023-180004
日期: 20160204,人员:Galatea(0002),考勤结果:考勤正常,53824-180213
日期: 20160205,人员:Galatea(0002),考勤结果:考勤正常,84509-180329
日期: 20160206,人员:Galatea(0002),考勤结果:非工作日不考勤,-
日期: 20160207,人员:Galatea(0002),考勤结果:非工作日不考勤,-
日期: 20160208,人员:Galatea(0002),考勤结果:考勤正常,85944-180114
日期: 20160209,人员:Galatea(0002),考勤结果:考勤正常,84509-235603
******** 考勤记录 END ********
END