MyBatis练习:根据考勤记录统计人员出勤情况

我的电脑操作系统版本为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练习:根据考勤记录统计人员出勤情况_第1张图片

现使用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

你可能感兴趣的:(java,oracle,mybatis,考勤记录)