8. Query Generator

배고픈 징징이 ㅣ 2023. 2. 16. 10:59

1. 설명

지금까지는 쿼리를 일일히 다 작성하였지만, 이제 자동으로 CRUD 쿼리를 자동으로 만들어주도록 한다.

특정 테이블 클래스를 보고 컬럼들을 가져올수도 있지만, 우리는 schema.json 이라는 파일에서
모든 테이블과 컬럼들을 정의하고 있기때문에, schema.json을 참조하여 쿼리를 생성한다.

이제 쿼리를 일일히 작성하지 않아도 손쉽게 쿼리를 호출할 수 있다.

 

2. schema.json

차후 schema.json 파일을 기준으로 어플리케이션 실행시 마다 DB Update를 진행하게될 예정이다.

REF는 참조하고있는 컬럼을 의미한다.

규칙은 [Table Name]_ID 이다.

 

schema.json

[
  {
    "name": "PASSWORD",
    "columns": [
      {
        "name": "USER_ID",
        "type": "REF"
      },
      {
        "name": "PASSWORD",
        "type": "VARCHAR",
        "size": 64
      }
    ]
  },
  {
    "name": "USER",
    "columns": [
      ...
    ]
  }
  
  ...
]

 

3. Json 파일 읽기

 SchemaManager는 schema.json  파일을 읽어 Table들의 정보를 저장해 놓고,

쿼리 생성기가 원하는 Table의 정보를 반환해 준다.

 

 

SchemaManager.java

public class SchemaManager {

    public static final SchemaManager INSTANCE = new SchemaManager();

    Map<String, Table> tables = new HashMap<>();

    private SchemaManager() {
        readFiles().eachObject(each -> {
            //table은 schema.json에 배열로 들어가있는 하나의 테이블이 된다.
            Table table = new Table(each);
            
            //table..getName()은 테이블의 이름이 들어가고, 값으로 테이블을 넣어준다.
            tables.put(table.getName(), table);
        });
    }

    public Table get(Class<? extends Model> modelType) {
        //쿼리 생성기에서 목적으로하는 Table과 관련된 객체를 보내면
        //위에서 저장한 tables에서 그 파일의 이름과 일치하는 Table을 반환한다.
        return tables.get(modelType.getSimpleName().toUpperCase());
    }

    public JArray readFiles() {
        try {
            Reader reader = new InputStreamReader(ApplicationLoader.class.getResource("schema.json").openStream(), StandardCharsets.UTF_8);
            JsonParser parser = new JsonParser();
            return new JArray(parser.parse(reader).getAsJsonArray());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

 

4. Select 쿼리 생성기

CRUD 쿼리 생성기들 중에 Read 부분인 Select 쿼리만 설명하도록 한다.

나머리 생성기들도 비슷하게 만들어진다.

먼저 schemaManager에서 Table 정보를 가져온다.

 

ModelSelector.java

public class ModelSelector {
    public JArray select(Class<? extends Model> className) {
        Connection connection = null;

        try {
            connection = Sqls.getConnection();
            JArray result = new JArray();

            Table table = SchemaManager.INSTANCE.get(className);
            String query = getSelectQuery(table);
            System.out.println("QUERY : " + query);

            PreparedStatement pstmt = connection.prepareStatement(query);
            ResultSet resultSet = pstmt.executeQuery();
            while (resultSet.next()) {
                JObject oneRow = new JObject();
                oneRow.add("id", resultSet.getLong("ID"));
                for (int k = 0; k < table.columns.size(); k++){
                    if (table.columns.get(k).isSystemColumn) continue;
                    String targetColumn = table.columns.get(k).getName();
                    oneRow.add(Simms.camelize(targetColumn), resultSet.getString(targetColumn));
                }

                result.addObject(oneRow);
            }

            return result;
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        } finally {
            Sqls.close(connection);
        }
    }
    
    public JArray select(Class<? extends Model> modelType, Condition condition) {
        ...

        if (condition != null && !condition.items.isEmpty()) {
            query += " WHERE ";
            boolean isStarted = false;
            for (ConditionItem item : condition.items) {
                if (isStarted) {
                    query += " AND ";
                } else {
                    isStarted = true;
                }
                query += item.toSql();
            }
        }
        System.out.println("QUERY : " + query);

        PreparedStatement pstmt = connection.prepareStatement(query);

        int i = 0;
        for (ConditionItem item : condition.items) {
            pstmt.setObject(++i, item.value);
        }

        ...
    }

    String getSelectQuery(Table table){
        StringBuilder result = new StringBuilder("SELECT ID");
        for (Column each : table.columns) {
            result.append(", ");
            result.append(each.getName());
        }

        result.append(" FROM " + table.getName());

        return result.toString();
    }
}

 

5. 사용

Condition과 ConditionItem 클래스에서는 Where절에 들어갈 조건들을 관리한다.

 

Condition.java

public class Condition {

    List<ConditionItem> items = new ArrayList<>();

    public void addEqual(String name, String value) {
        items.add(new ConditionItem(name, value, ConditionType.EQUAL));
    }

    ...
}

 

ConditionItem.java

public class ConditionItem {
    String name;
    Object value;
    ConditionType type;

    public ConditionItem(String name, Object value, ConditionType type) {
        this.name = name;
        this.value = value;
        this.type = type;
    }

    public String toSql() {
        return String.format("%s = ?", Simms.underscore(name));
    }
}

 

UserController.java

...

public Object login(JObject json, HttpSession session) {
    JObject u = Models.selectOne(User.class, condition -> {
        condition.addEqual("userId", json.getString("userId"));
    });

    ...
}

...

 

Models.java

...

public static JArray select(Class<? extends Model> modelType, Consumer<Condition> consumer) {
    Condition condition = new Condition();
    consumer.accept(condition);

    return new ModelSelector().select(modelType, condition);
}

public static JObject selectOne(Class<? extends Model> modelType, Consumer<Condition> consumer) {
    JArray result = select(modelType, consumer);
    return result.size() > 0 ? result.getObject(0) : null;
}

...

 

반응형

'Java > - Pure Java Project' 카테고리의 다른 글

9. Fetch & XMLHttpRequest  (0) 2023.03.06
7. LocalThread & Transaction ( Runnable )  (0) 2023.02.13
6. Annotation  (0) 2023.02.09
5. JNDI : DB Connect  (0) 2023.02.08
4. Router (Dynamic Import & Create Class)  (0) 2023.02.07