반응형

C# WPF로 작업하던 프로그램이 오류를 뱉는데, 디버깅이 안되어 Java로 전부 바꾸는 실험을 했다. WPF는 Visual Studio Code에서 만든거라, 디버깅이 좀 힘들었다. 반면에 Java는 무료이면서도 강력한 IntelliJ가 있다. 물론 이클립스도 있다. 

이번엔 IntelliJ로 프로젝트를 만들었는데, 사실 그것도 처음이라 좀 많이 힘들었다.

 

이번 프로젝트는 출입 통제하는 프로그램이다. 자료실에 드나드는 사람에게 ID 태그를 읽혀서 들어올땐 "안녕하세요", 나갈땐 "안녕히가세요"를 출력해주고, 다른 탭에서 전체 출입인원 목록을 확인하면 된다.

 

프로젝트 구성은 아래와 같다. 

Spring Boot를 해본 사람이라면 좀 익숙할 수도 있겠다. 그것과 비슷한 구조로 만들려고 신경을 써봤다. 각 파일들의 코드를 기록으로 남기고자 한다. 참고하려는 분들은 아래의 프로젝트 구성도를 계속 확인하면서 보면 좋겠다.

 

1. pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  .
  .
  .
   <!-- MyBatis -->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.5.10</version>
    </dependency>
    
    <!-- OraCLE.. 두가지가 다 필요했다. -->
    <dependency>
      <groupId>com.oracle.database.jdbc</groupId>
      <artifactId>ojdbc11</artifactId>
      <version>23.5.0.24.07</version>
    </dependency>

    <dependency>
      <groupId>com.oracle.ojdbc</groupId>
      <artifactId>orai18n</artifactId>
      <version>19.3.0.0</version>
    </dependency>

    <!-- Serial 통신 라이브러리 -->
    <dependency>
      <groupId>com.fazecast</groupId>
      <artifactId>jSerialComm</artifactId>
      <version>2.11.0</version>
    </dependency>

  </dependencies>

  ...

 

 

2. 화면구성(hello-view.fxml)

화면은 단순하다. 그렇지만 수많은 시행착오끝에 만든 화면이다. JavaFX로 만들어보는건 처음이라..

<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.String?>
<?import javafx.collections.FXCollections?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ComboBox?>
<?import javafx.scene.control.DatePicker?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.Tab?>
<?import javafx.scene.control.TabPane?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?>

<TabPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="800.0" prefWidth="1400.0" tabClosingPolicy="UNAVAILABLE" xmlns="http://javafx.com/javafx/23.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.example.javafx05.HelloController">
  <tabs>
    <Tab text="Home">
      <content>
        <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0">
          <children>
            <VBox alignment="TOP_CENTER" prefHeight="571.0" prefWidth="800.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
              <children>
                <Label alignment="CENTER" contentDisplay="CENTER" prefHeight="64.0" prefWidth="1000.0" style="-fx-font-weight: bold; -fx-text-fill: #2c3e50;" text="DATA ROOM SECURITY" textAlignment="CENTER">
                  <font>
                    <Font name="System Bold" size="72.0" />
                  </font>
                           <VBox.margin>
                              <Insets bottom="20.0" top="20.0" />
                           </VBox.margin>
                </Label>
                <Label fx:id="welcomeText" alignment="CENTER" contentDisplay="CENTER" prefHeight="200.0" prefWidth="1200.0" style="-fx-text-fill: #3498db;" text="입/퇴장시 체크해주세요" textAlignment="CENTER">
                  <font>
                    <Font name="System Bold" size="96.0" />
                  </font>
                           <VBox.margin>
                              <Insets bottom="60.0" top="30.0" />
                           </VBox.margin>
                </Label>
                <HBox alignment="CENTER" prefHeight="278.0" prefWidth="1400.0">
                  <children>
                    <VBox alignment="CENTER" prefHeight="334.0" prefWidth="500.0">
                      <children>
                            <GridPane alignment="CENTER" prefHeight="293.0" prefWidth="500.0">
                              <columnConstraints>
                                <ColumnConstraints hgrow="SOMETIMES" maxWidth="194.0" minWidth="10.0" prefWidth="103.0" />
                                <ColumnConstraints hgrow="SOMETIMES" maxWidth="297.0" minWidth="10.0" prefWidth="297.0" />
                              </columnConstraints>
                              <rowConstraints>
                                <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
                                <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
                                <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
                                  <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
                                  <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
                              </rowConstraints>
                               <children>
                                  <Button alignment="CENTER" contentDisplay="RIGHT" mnemonicParsing="false" prefHeight="50.0" prefWidth="200.0" text="Purpose" GridPane.halignment="CENTER">
                                             <font>
                                                <Font size="24.0" />
                                             </font></Button>
                                  <Button alignment="CENTER" mnemonicParsing="false" prefHeight="50.0" prefWidth="200.0" text="Location" textAlignment="CENTER" wrapText="true" GridPane.halignment="CENTER" GridPane.rowIndex="1">
                                             <font>
                                                <Font size="24.0" />
                                             </font></Button>
                                  <Button alignment="CENTER" mnemonicParsing="false" prefWidth="200.0" text="DEPT" GridPane.halignment="CENTER" GridPane.rowIndex="2">
                                             <font>
                                                <Font size="24.0" />
                                             </font></Button>
                                  <Button alignment="CENTER" mnemonicParsing="false" prefWidth="200.0" text="ID" GridPane.halignment="CENTER" GridPane.rowIndex="3">
                                             <font>
                                                <Font size="24.0" />
                                             </font></Button>
                                  <Button alignment="CENTER" mnemonicParsing="false" prefWidth="200.0" text="NAME" GridPane.halignment="CENTER" GridPane.rowIndex="4">
                                             <font>
                                                <Font size="24.0" />
                                             </font></Button>
                                  <ComboBox fx:id="purpose_combo" prefHeight="50.0" prefWidth="300.0" promptText="도서 열람" style="-fx-alignment: CENTER; -fx-font-size: 18px; -fx-font-weight: bold;" GridPane.columnIndex="1" GridPane.rowIndex="0">
                                    <items>
                                      <FXCollections fx:factory="observableArrayList">
                                        <String fx:value="도서 열람" />
                                        <String fx:value="도서 대출" />
                                        <String fx:value="도서 반납" />
                                        <String fx:value="스캔" />
                                        <String fx:value="기타" />
                                      </FXCollections>
                                    </items>
                                  </ComboBox>
                                  <ComboBox fx:id="location_combo" prefHeight="50.0" prefWidth="300.0" promptText="무인기사업부자료실" style="-fx-font-size: 18px;" GridPane.columnIndex="1" GridPane.rowIndex="1">
                                    <items>
                                    <FXCollections fx:factory="observableArrayList">
                                      <String fx:value="무인기사업부자료실" />
                                      <String fx:value="군용기정비자료실" />
                                      <String fx:value="품질경영부" />
                                    </FXCollections>
                                  </items>
                                 </ComboBox>
                                  <TextField fx:id="dept_field" prefHeight="50.0" prefWidth="300.0" GridPane.columnIndex="1" GridPane.rowIndex="2">
                                             <font>
                                                <Font size="18.0" />
                                             </font></TextField>
                                  <TextField fx:id="id_field" prefHeight="50.0" prefWidth="300.0" GridPane.columnIndex="1" GridPane.rowIndex="3">
                                             <font>
                                                <Font size="18.0" />
                                             </font></TextField>
                                  <TextField fx:id="name_field" prefHeight="50.0" prefWidth="300.0" GridPane.columnIndex="1" GridPane.rowIndex="4">
                                             <font>
                                                <Font size="18.0" />
                                             </font></TextField>
                               </children>
                            </GridPane>
                      </children>
                    </VBox>
                  </children>
                </HBox>
              </children>
            </VBox>
          </children></AnchorPane>
      </content>
    </Tab>
      <Tab text="History">
          <content>
              <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="794.0" prefWidth="1400.0">
                  <children>
                      <VBox prefHeight="778.0" prefWidth="1400.0" AnchorPane.bottomAnchor="-8.0" AnchorPane.leftAnchor="2.0" AnchorPane.rightAnchor="-2.0" AnchorPane.topAnchor="1.0">
                          <children>
                              <GridPane alignment="CENTER_RIGHT">
                                  <columnConstraints>
                                      <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
                                      <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
                                      <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
                                      <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
                                      <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
                                      <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
                                  </columnConstraints>
                                  <rowConstraints>
                                      <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
                                      <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
                                      <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
                                  </rowConstraints>
                                  <children>
                                      <Label text="이름" GridPane.halignment="RIGHT">
                                 <GridPane.margin>
                                    <Insets right="10.0" />
                                 </GridPane.margin></Label>
                                      <Label text="Purpose" GridPane.halignment="RIGHT" GridPane.rowIndex="1">
                                 <GridPane.margin>
                                    <Insets right="10.0" />
                                 </GridPane.margin></Label>
                                      <Label text="Location" GridPane.halignment="RIGHT" GridPane.rowIndex="2">
                                 <GridPane.margin>
                                    <Insets right="10.0" />
                                 </GridPane.margin></Label>
                                      <Label text="From" GridPane.columnIndex="2" GridPane.halignment="RIGHT">
                                 <GridPane.margin>
                                    <Insets right="10.0" />
                                 </GridPane.margin></Label>
                                      <Label text="To" GridPane.columnIndex="2" GridPane.halignment="RIGHT" GridPane.rowIndex="1">
                                 <GridPane.margin>
                                    <Insets right="10.0" />
                                 </GridPane.margin></Label>
                                      <TextField fx:id="search_name" GridPane.columnIndex="1" />
                                      <ComboBox fx:id="search_purpose" prefHeight="17.0" prefWidth="297.0" promptText="도서 열람" GridPane.columnIndex="1" GridPane.rowIndex="1">
                                          <items>
                                              <FXCollections fx:factory="observableArrayList">
                                                  <String fx:value="" />
                                                  <String fx:value="도서 열람" />
                                                  <String fx:value="도서 대출" />
                                                  <String fx:value="도서 반납" />
                                                  <String fx:value="스캔" />
                                                  <String fx:value="기타" />
                                              </FXCollections>
                                          </items>
                                      </ComboBox>
                                      <ComboBox fx:id="search_location" prefHeight="17.0" prefWidth="297.0" promptText="무인기사업부자료실" GridPane.columnIndex="1" GridPane.rowIndex="2">
                                          <items>
                                              <FXCollections fx:factory="observableArrayList">
                                                  <String fx:value="" />
                                                  <String fx:value="무인기사업부자료실" />
                                                  <String fx:value="군용기정비자료실" />
                                                  <String fx:value="품질경영부" />
                                              </FXCollections>
                                          </items>
                                      </ComboBox>
                                      <DatePicker fx:id="search_from" GridPane.columnIndex="3" />
                                      <DatePicker fx:id="search_to" GridPane.columnIndex="3" GridPane.rowIndex="1" />
                                      <Button fx:id="history_search" minWidth="100.0" mnemonicParsing="false" onAction="#history_search_click" text="검색" GridPane.columnIndex="5" />
                                      <Button fx:id="initiate_search" minWidth="100.0" mnemonicParsing="false" onAction="#initiate_search_click" text="초기화" GridPane.columnIndex="5" GridPane.rowIndex="1" />
                                  </children>
                                  <VBox.margin>
                                      <Insets top="20.0" />
                                  </VBox.margin>
                              </GridPane>
                              <TableView fx:id="historyTable" prefHeight="500.0" prefWidth="1400.0" VBox.vgrow="ALWAYS">
                                  <columns>
                                      <TableColumn fx:id="colEmpNo" minWidth="100.0" prefWidth="150.0" text="사번" style="-fx-alignment:CENTER"/>
                                      <TableColumn fx:id="colName" minWidth="100.0" prefWidth="150.0" text="이름"  style="-fx-alignment:CENTER"/>
                                      <TableColumn fx:id="colDept" minWidth="100.0" prefWidth="150.0" text="부서"  style="-fx-alignment:CENTER"/>
                                      <TableColumn fx:id="colIn_D" minWidth="200.0" prefWidth="250.0" sortType="DESCENDING" text="입장" />
                                      <TableColumn fx:id="colOut_D" minWidth="200.0" prefWidth="250.0" text="퇴장" />
                                      <TableColumn fx:id="colPurpose" minWidth="150.0" prefWidth="200.0" text="방문목적" />
                                      <TableColumn fx:id="colLocation" minWidth="200.0" prefWidth="300.0" resizable="true" text="위치" />
                                  </columns>
                                  <VBox.margin>
                                      <Insets top="15.0" />
                                  </VBox.margin>
                              </TableView>
                          </children>
                      </VBox>
                  </children></AnchorPane>
          </content>
      </Tab>

  </tabs>
</TabPane>

 

3. module-info.java

JavaFX에서 모듈을 인식하기 위해서는 module-info.java가 필요하다. 한 프로젝트 안에서 동작하는 것임에도 손으로 하나씩 추가해야하는 부분이 많다. 뭐 하나라도 빠지면 동작을 안해서 난감했다.

module com.example.javafx05 {
    requires javafx.controls;
    requires javafx.fxml;
    requires org.mybatis;  <--추가
    requires java.sql;     <--추가
    requires com.fazecast.jSerialComm;  <--추가


    opens com.example.javafx05 to javafx.fxml;
    exports com.example.javafx05;
    exports com.example.javafx05.emp to org.mybatis;     <--추가(Mapper용)
    exports com.example.javafx05.history to org.mybatis; <--추가(Mapper용)

    opens mapper;<--추가(Mapper용)
}

 

 

4. Database접속(mybatis-config.xml)

이 파일은 특별할 건 없다. 형식에 맞춰서 입력만 해주면 된다.

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="oracle.jdbc.driver.OracleDriver"/>
                <property name="url" value="jdbc:oracle:thin:@10.48.63.71:1526:database_name"/>
                <property name="username" value="my_user_name"/>
                <property name="password" value="my_user_pw"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="mapper/empMapper.xml"/>
        <mapper resource="mapper/historyMapper.xml"/>
    </mappers>
</configuration>

 

5. MyBatisUtil.java

MyBatis를 사용시 데이터베이스 연결을 관리하는 파일이다. 

package com.example.javafx05;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;

public class MyBatisUtil {
    private static SqlSessionFactory sqlSessionFactory;

    static {
        try {
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static SqlSessionFactory getSqlSessionFactory() {
        return sqlSessionFactory;
    }

    public static SqlSession getSession() {
        return sqlSessionFactory.openSession();
    }
}

 

6. HelloApplication

시작점이 되는 파일이다. 코드 아래쪽을 보면 SerialService라는 서비스를 불러내는데, controller를 인자로 넘겨준다. controller는 화면 구성요소와 그에 대한 controll을 하는 부분이다. 이 controller를 넘겨줘야 Serial 데이터의 입력에 맞춰 화면에 정보를 뿌릴 수 있다. 

package com.example.javafx05;

import com.example.javafx05.emp.EmpDto;
import com.example.javafx05.emp.EmpService;
import com.fazecast.jSerialComm.SerialPort;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.stage.Stage;

import java.io.IOException;

public class HelloApplication extends Application {

    @Override
    public void start(Stage stage) throws IOException {
        FXMLLoader loader = new FXMLLoader(HelloApplication.class.getResource("hello-view.fxml"));
        Scene scene = new Scene(loader.load(), 900, 800);
        HelloController controller = loader.getController();

        stage.setTitle("Data Room Security");
        stage.setScene(scene);
        stage.show();

        SerialService serialService = new SerialService(controller);  //<-- 시리얼 통신 + UI 컨트롤
        serialService.initializeSerialCommunication();                //<-- 시리얼 통신 + UI 컨트롤
    }

    public static void main(String[] args) {
        launch();
    }
}

 

7. SerialService.java

위의 HelloApplication에서 시작하여 Serial데이터를 받기 시작하는 서비스이다. 데이터를 너무 빨리 처리하면 Serial데이터가 두번에 나눠져 들어온다. 그래서 초기에 약간의 대기시간(50ms)을 두었으며, 그래도 모를 예외를 대비해서 12자리를 모두 채우면 데이터를 처리하도록 만들었다.

 

데이터(message)가 취득되면 emp_check이라는 직원 체크 함수로 간다. 이 함수에서 MyBatis를 통한 DB데이터를 가져오고, 최종 만들어진 데이터 및 인사말은 

```Platform.runLater(() -> controller.updateUI(emp, finalHello));``` 

부분에서 화면으로 전달된다.

 

package com.example.javafx05;

import com.example.javafx05.emp.EmpDto;
import com.example.javafx05.emp.EmpService;
import com.fazecast.jSerialComm.SerialPort;
import javafx.application.Platform;
import javafx.scene.control.Alert;

import java.util.HashMap;
import java.util.Map;

public class SerialService {

    private final HelloController controller;
    private final EmpService empService = new EmpService();
    private SerialPort serialPort;
    private String message = "";

    public SerialService(HelloController controller){
        this.controller = controller;
    }

    public void initializeSerialCommunication(){
        SerialPort[] ports = SerialPort.getCommPorts();
        if (ports.length ==0){
            System.out.println("No serial ports available");
            return;
        }

        serialPort = ports[0];
        serialPort.setComPortParameters(9600, 8, 1, 0);
        serialPort.openPort();
        System.out.println("Serial port opend: " + serialPort.getSystemPortName());

        // 데이터 수신 스레드 시작
        new Thread(() -> {
            while(serialPort.isOpen()){
                try{
                    if (serialPort.bytesAvailable() > 0) {
                        Thread.sleep(50); // 짧은 시간 대기
                        byte[] readBuffer = new byte[serialPort.bytesAvailable()];
                        int numRead = serialPort.readBytes(readBuffer, readBuffer.length);
                        String receivedData = new String(readBuffer, 0, numRead);
                        message += receivedData;
                        // 12자리 데이터가 모두 수신되었는지 확인
                        if (message.length() >= 12) {
//                            showPopup(message.substring(4, 11)); // 필요한 길이만큼만 팝업에 표시
                            emp_check(message.substring(4, 11));
                            message = "";   // message 초기화
                            Thread.sleep(3000); // 3초
                            init_screen();
                        }
                    } else {
                        // 데이터가 일정 시간 동안 들어오지 않으면 (예: 50ms) message 초기화
                        // 이는 데이터가 끊겨서 들어올 때 이전 데이터와 합쳐지는 것을 방지합니다.
                        try {
                            Thread.sleep(10); // 짧은 시간 대기
                        } catch (InterruptedException e) {
                            Thread.currentThread().interrupt();
                        }
                        if (serialPort.bytesAvailable() == 0 && !message.isEmpty()) {
                            message = "";
                        }
                    }
                } catch (Exception e){
                    e.printStackTrace();
                }
            }
        }).start();
    }


    private void emp_check(String emp_n){
        EmpDto emp = empService.selectAll(emp_n);

        Map<String, String> history = new HashMap<String, String>();
        history = empService.check_history(emp_n);
        String purpose= controller.getSelectedPurpose();
        String location=controller.getSelectedLocation();

        String hello="";
        if(history==null){
            hello="안녕하세요";
            empService.in_log(emp_n, purpose, location);
        }else{
            hello="안녕히가세요";
            empService.out_log(emp_n);
        }

        final String finalHello=hello;
        Platform.runLater(() -> controller.updateUI(emp, finalHello));
    }

    private void init_screen(){
        Platform.runLater(() -> controller.updateUI(null,"입/퇴장시 체크해주세요"));
    }

}

 

8. EmpService (직원정보 관련 서비스)

package com.example.javafx05.emp;

import com.example.javafx05.MyBatisUtil;
import org.apache.ibatis.session.SqlSession;

import java.util.HashMap;
import java.util.Map;

public class EmpService {
    public EmpDto selectAll(String emp_number){
        SqlSession session = MyBatisUtil.getSession();
        EmpDto emp_found = null;

        try{
            EmpMapper mapper = session.getMapper(EmpMapper.class);
            emp_found = mapper.select_member(emp_number);
//            System.out.println(emp_found.toString());
        } catch (Exception ex){
            ex.printStackTrace();
        } finally {
            session.close();
        }
        return emp_found;
    }

    public Map<String, String> check_history(String emp_number){
        SqlSession session = MyBatisUtil.getSession();
        Map<String, String> result = new HashMap<String, String>();

        try{
            EmpMapper mapper = session.getMapper(EmpMapper.class);
            result = mapper.history_found(emp_number);
        } catch (Exception ex){
            ex.printStackTrace();
        } finally {
            session.close();
        }
        return result;
    }

    public void in_log(String emp_number, String purpose, String location){
        SqlSession session = MyBatisUtil.getSession();
        try{
            EmpMapper mapper = session.getMapper(EmpMapper.class);
            mapper.history_in_log(emp_number, purpose, location);
        } catch (Exception ex){
            ex.printStackTrace();
        } finally {
            session.close();
        }
        return;
    }

    public void out_log(String emp_number){
        SqlSession session = MyBatisUtil.getSession();
        try{
            EmpMapper mapper = session.getMapper(EmpMapper.class);
            mapper.history_out_log(emp_number);
        } catch (Exception ex){
            ex.printStackTrace();
        } finally {
            session.close();
        }
        return;
    }

}

 

9. EmpDto 

package com.example.javafx05.emp;

public record EmpDto (
    String emp_no,
    String emp_x,
    String kornm_n,
    String hannm_n,
    String engnm_n,
    String res,
    String dept_c,
    String dept_n
) {
}

 

10. EmpMapper

package com.example.javafx05.emp;

import org.apache.ibatis.annotations.Param;

import java.util.Map;

public interface EmpMapper {
    EmpDto select_member(String emp_no);
    Map<String, String> history_found(String emp_no);
    void history_in_log(@Param("emp_no") String emp_no,@Param("purpose")  String purpose,@Param("location") String location);
    void history_out_log(String emp_no);
}

 

 

11. empMapper.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="com.example.javafx05.emp.EmpMapper">
    <select id="select_member" resultType="com.example.javafx05.emp.EmpDto">
        select emp_# as emp_no,
        emp_x,
        kornm_n,
        hannm_n,
        engnm_n,
        res_#1 as res,
        dept_c,
        dept_n
        from bemp a
        where a.emp_#=#{emp_no}
    </select>

    <select id="history_found" resultType="map">
        select EMP_# as emp_no,
        IN_D,
        OUT_D,
        PURPOSE,
        LOCATION
        FROM BEMP_DATAROOM_HSTRY a
        where a.emp_#=#{emp_no}
        AND to_char(a.in_d,'YYYYMMDD')=to_char(SYSDATE,'YYYYMMDD')
        AND a.OUT_D IS null
        ORDER BY a.IN_D DESC
    </select>

    <select id="history_in_log">
        insert into BEMP_DATAROOM_HSTRY (emp_#, in_d, out_d, purpose, location)
        values (#{emp_no}, sysdate,NULL,#{purpose},#{location})
    </select>

    <select id="history_out_log">
        update BEMP_DATAROOM_HSTRY a
        set a.out_d=sysdate
        where a.emp_#=#{emp_no}
        and a.out_d IS NULL
        AND to_char(a.in_d,'YYYYMMDD')=to_char(SYSDATE,'YYYYMMDD')
    </select>
</mapper>

 

12. HistoryDto

package com.example.javafx05.history;

public record HistoryDto( String emp_no,
                          String kornm_n,
                          String dept_c,
                          String in_d,
                          String out_d,
                          String purpose,
                          String location) {
}

 

13. HistoryMapper

package com.example.javafx05.history;

import org.apache.ibatis.annotations.Param;

import java.time.LocalDate;
import java.util.List;

public interface HistoryMapper {
    List<HistoryDto> historyRecords(@Param("emp_name") String emp_name,
                                    @Param("purpose") String purpose,
                                    @Param("location")  String location,
                                    @Param("from")  String from,
                                    @Param("to")  String to); //,, LocalDate to
}

 

14. HistoryService

package com.example.javafx05.history;

import com.example.javafx05.MyBatisUtil;
import org.apache.ibatis.session.SqlSession;

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;

public class HistoryService {
    public List<HistoryDto> find_history(String emp_name, String purpose, String location, String from, String to){
        SqlSession session = MyBatisUtil.getSession();
        List<HistoryDto> result = new ArrayList<>();

        try{
            HistoryMapper mapper = session.getMapper(HistoryMapper.class);
            result = mapper.historyRecords(emp_name, purpose, location, from, to);
        } catch (Exception ex){
            ex.printStackTrace();
        } finally {
            session.close();
        }
        return result;
    }
}

 

15. historyMapper.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="com.example.javafx05.history.HistoryMapper">
    <select id="historyRecords" resultType="com.example.javafx05.history.HistoryDto">
        select a.emp_# ,
        b.KORNM_N ,
        b.dept_c,
        a.in_d,
        a.out_d,
        a.purpose,
        a.location
        from temp_dataroom_hstry a, temp b
        where a.emp_#=b."EMP_#"
        <if test="emp_name!=null and emp_name!=''">
            and b.kornm_n like '%'||#{emp_name}||'%'
        </if>
        <if test="purpose!=null and purpose!=''">
            and a.purpose like '%'||#{purpose}||'%'
        </if>
        <if test="location!=null and location!=''">
            and a.location like '%'||#{location}||'%'
        </if>
        <if test="from!=null and from!=''">
            and a.in_d &gt; to_date(#{from},'YYYY-MM-DD')
        </if>
        <if test="to!=null and to!=''">
            and a.in_d &lt; to_date(#{to},'YYYY-MM-DD')
        </if>
        order by a.in_d desc
    </select>

</mapper>

 

 

[결과 화면 ]

 

반응형

+ Recent posts