個人電腦操做系統版本爲Win7旗艦版(ServicePack1),Oracle版本爲Oracle11gjava
程序使用的jar包有:mybatis-3.2.2.jar、ojdbc14-10.2.0.2.0.jarsql
本例中使用的配置文件mybatis-config.xml,能夠參見個人另外一篇Blog《一個簡單的MyBatis鏈接Oracle數據庫的例子》(http://my.oschina.net/Tsybius2014/blog/626206)數據庫
本文仍是一個使用MyBatis進行查詢的練習之做。apache
現有三張表:session
1)OPERATOR表(簽到人員信息表)記錄了人員的ID和名稱,該表中每一個打卡人都做爲一條數據存入mybatis
2)WORKDAY表(工做日曆表)記錄了工做日曆,該表中每日都做爲一條數據存入,INFO_DATE列爲日期,IS_WORK_DAY列爲1時爲工做日,爲0時爲非工做日app
3)CLOCKING_IN_DATA表(打卡數據表)記錄了考勤打卡狀況,每次打卡向該表中插入一條數據,OPER_DATE爲打卡日期,OPER_TIME爲打卡時間,OPER_ID爲打卡人ID,OPER_NAME爲打卡人姓名測試
要求根據這三張表,計算出指定人的出勤狀況。ui
使用下面的SQL腳本,能夠向Oracle數據庫中插入基礎數據:spa
-- 簽到人員信息表 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-五、八、9是工做日,六、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)的出勤記錄
統計規則以下:
一、一日的時間從當日0點起,至當日235959點止,每日統計最先的一次和最晚的一次打卡記錄
二、第一次打卡事件晚於9點鐘斷定爲遲到,最後一次打卡時間早於18點鐘算早退
三、一天打卡次數少於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