간단한 유니버설 다오 층 도구

12698 단어
레이어 디자인
통용적인 사고는dao층이 데이터베이스와 표와 무관하게 어떻게 해야 하는지는 매우 고려할 만한 문제이다. 게으름을 피우고 모든 업무를 위해 특정한dao층을 작성하여 실현하지 않으려면 발생할 수 있는 모든 상황을 고려해야 한다.sql문장에 대해 이야기하고자 하는 것은 통용이냐 통용이냐가 아니다. 왜냐하면 sql문장은 이미 가장 통용되고 가장 유연한 데이터베이스 조작이 이루어졌기 때문이다. 왜냐하면 너는 그것으로 어떠한 데이터베이스를 조작할 수 있기 때문이다. 네가 대응하는 sql문장을 쓰기만 하면 된다.그러나 바로 sql문장이 너무 유연하기 때문에 작성이 매우 번거롭고 업무의 차이로 인해 데이터베이스 테이블이 다르면 sql문장도 반드시 다르다. 설령 하나의 업무가 간단하다 하더라도 이전에 쓴 sql문장을 거의 다시 사용할 수 없다.
따라서 제가 프로그램에서 해야 할 일은 자바 코드의 사고방식으로 데이터베이스 조작을 실현하고 사용자에게 너무 많은 실현 세부 사항을 노출하지 않는 것입니다. 이른바 너무 많은 세부 사항을 노출하지 않는 것은 방법의 매개 변수가 너무 복잡하지 않고 방법을 간단하고 쉽게 사용할 수 있으며 자주 발생하는 업무를 만족시킬 수 있다는 것을 말합니다.밑바닥은 당연히 sql를 연결하는 형식을 사용하는데 최종적으로 얻은 효과는 sql문장을 잘 모르더라도 자바를 알고 sql를 쓰지 않으면 데이터베이스를 조작할 수 있다는 것이다.
그래서 나는 이 인터페이스의 설계를 가지게 되었다.
/**
 * Created by liuruijie on 2017/1/17.
 *                dao   
 */
public interface CommonDao {
    /**
     *           
     * @param tableName   
     * @param pkName      
     * @param id  
     * @param type         
     * @return        po    
     */
    T selectById(String tableName, String pkName, String id, Class type);

    /**
     *           
     * List students = commonDao
     *                      .selectByCriteria("m_student"
     *                      , commonDao.createCriteria()
     *                              .not().like("id", "2013")
     *                              .between("age", 10, 20)
     *                              .not().eq("gender", "F")
     *                      , Student.class);
     * @param tableName   
     * @param criteria     
     * @param type   
     * @return        po       
     */
    List selectByCriteria(String tableName, Criteria criteria, Class type);

    /**
     *      
     * @param tableName   
     * @param criteria     
     * @return    
     */
    long countByCriteria(String tableName, Criteria criteria);

    /**
     *           
     * @param tableName   
     * @param pkName      
     * @param id    
     * @return      0 1
     */
    int removeById(String tableName, String pkName, String id);

    /**
     *               
     *          ,    
     *           ,    
     * @param tableName   
     * @param pkName      
     * @param entity         
     * @return      0 1
     */
    int save(String tableName, String pkName, T entity);

    /**
     *     
     */
    interface Criteria{
        /**
         *          
         */
        Criteria not();

        /**
         *              or,   and
         */
        Criteria or();

        /**
         *   
         * @param field    
         * @param val  
         */
        Criteria eq(String field, Object val);

        /**
         *      
         * @param field    
         * @param val  
         */
        Criteria like(String field, Object val);

        /**
         *         
         * @param field    
         * @param val1  1
         * @param val2  2
         */
        Criteria between(String field, Object val1, Object val2);

        /**
         *        
         * @param start     
         * @param row    
         */
        Criteria limit(int start, int row);

        /**
         *       
         * @return     
         */
        List getParam();

        /**
         *       where  sql  
         * @return sql
         */
        StringBuilder getCriteriaSQL();
    }

    /**
     *                
     * @return       
     */
    Criteria createCriteria();
}

이 인터페이스는 가장 자주 사용하는 몇 가지 조작을 제공했다. 메인 키 조회 기록, 다조건 조회, 한 개의 기록 삭제, 갱신 기록과 삽입 기록, 그리고 대부분의 업무를 만족시킬 수 있고 사용자가 제공하는 파라미터도 많지 않다.물론 간단하게 하려면 흔히 볼 수 없는 세부 사항을 버려야 한다. 예를 들어 여기서 나는 단일 키의 상황만 고려했다.물론 이것들은 끊임없이 보완될 수 있기 때문에 처음부터 완전무결한 것을 만드는 것은 틀림없이 불가능하다.다음은 실현을 살펴보겠습니다.
/**
 * Created by liuruijie on 2017/1/17.
 *   dao      
 */
@Service
public class CommonDaoImpl implements CommonDao{
    @Resource
    JdbcTemplate jdbcTemplate;

    @Override
    public T selectById(String tableName, String pkName, String id, Class type) {
        Map obj = jdbcTemplate.queryForMap("SELECT * FROM "+tableName+" WHERE "+pkName+" = ?", id);
        return ObjectUtil.mapToObject(obj, type);
    }

    @Override
    public List selectByCriteria(String tableName, CommonDao.Criteria criteria, Class type) {
        StringBuilder sqlStr = new StringBuilder("");
        sqlStr.append("SELECT * FROM ")
                .append(tableName)
                .append(criteria.getCriteriaSQL());
        System.out.println(sqlStr.toString());
        Object[] params = criteria.getParam().toArray(new Object[criteria.getParam().size()]);
        List> objs = jdbcTemplate.queryForList(sqlStr.toString(), params);
        List results = new ArrayList<>();
        for(Map o: objs){
            results.add(ObjectUtil.mapToObject(o, type));
        }
        return results;
    }

    @Override
    public long countByCriteria(String tableName, CommonDao.Criteria criteria) {
        String sql = "SELECT COUNT(*) AS num FROM "+tableName + criteria.getCriteriaSQL();
        Map map = jdbcTemplate.queryForMap(sql, criteria.getParam().toArray());
        return (Long)map.get("num");
    }

    @Override
    public int removeById(String tableName, String pkName, String id) {
        String sql = "DELETE FROM " +
                tableName +
                " WHERE " +
                pkName +
                " = ?";
        return jdbcTemplate.update(sql, id);
    }

    @Override
    public int save(String tableName, String pkName, T entity) {
        Map obj = ObjectUtil.objectToMap(entity);
        StringBuffer sql1 = new StringBuffer("INSERT INTO ")
                .append(tableName)
                .append("(");
        StringBuffer sql2 = new StringBuffer(" VALUES(");
        List args = new ArrayList<>();
        int count = 0;
        for(String key: obj.keySet()){
            Object arg = obj.get(key);
            if (arg==null){
                continue;
            }
            sql1.append(key).append(",");
            sql2.append("?,");
            args.add(arg);
        }
        sql1.deleteCharAt(sql1.length() - 1);
        sql1.append(") ");
        sql2.deleteCharAt(sql2.length() - 1);
        sql2.append(") ");
        String sql = sql1.append(sql2).toString();
        System.out.println(sql);
        try {
            count += jdbcTemplate.update(sql, args.toArray());
        }catch (DuplicateKeyException e){
            sql1 = new StringBuffer("UPDATE ")
                    .append(tableName)
                    .append(" SET ");
            sql2 = new StringBuffer(" WHERE "+pkName+"=?");
            args = new ArrayList<>();
            for (String key: obj.keySet()){
                if (key.equals(pkName)){
                    continue;
                }
                Object arg = obj.get(key);
                if (arg==null){
                    continue;
                }
                sql1.append(key).append("=?,");
                args.add(arg);
            }

            sql1.deleteCharAt(sql1.length() - 1);
            args.add(obj.get(pkName));
            sql = sql1.append(sql2).toString();
            System.out.println(sql);
            count+=jdbcTemplate.update(sql, args.toArray());
        }
        return count;
    }

    @Override
    public CommonDao.Criteria createCriteria() {
        return new Criteria();
    }


    /**
     *        
     */
    class Criteria implements CommonDao.Criteria{
        private boolean not; //      
        private boolean begin; //           
        private boolean or;//        OR
        StringBuilder criteriaSQL; // where     sql
        List param; //    
        String limitStr; //    

        public Criteria(){
            criteriaSQL = new StringBuilder("");
            param = new LinkedList<>();
            not = false;
            begin = true;
            limitStr = "";
        }

        public Criteria not(){
            not = true;
            return this;
        }

        @Override
        public CommonDao.Criteria or() {
            or = true;
            return this;
        }

        private void link(){
            //          
            // ,     WHERE     
            // ,         
            if(begin){
                criteriaSQL.append(" WHERE ");
            }else{
                if(or){
                    criteriaSQL.append(" OR ");
                }else{
                    criteriaSQL.append(" AND ");
                }
            }
            or = false;
        }

        public Criteria eq(String field, Object val) {
            link();
            if (not) {
                criteriaSQL.append(field)
                        .append(" != ?");
            } else {
                criteriaSQL.append(field)
                        .append(" = ?");
            }
            not = false;
            begin = false;
            param.add(val);
            return this;
        }

        public Criteria like(String field, Object val){
            link();
            if(not){
                criteriaSQL.append(field)
                        .append(" NOT LIKE ?");
            }else{
                criteriaSQL.append(field)
                        .append(" LIKE ?");
            }
            not = false;
            begin = false;
            param.add("%"+val+"%");
            return this;
        }
        public Criteria between(String field, Object val1, Object val2){
            link();
            if(not){
                criteriaSQL.append(field)
                        .append(" NOT BETWEEN ? AND ? ");
            }else{
                criteriaSQL.append(field)
                        .append(" BETWEEN ? AND ? ");
            }
            not = false;
            begin = false;
            param.add(val1);
            param.add(val2);
            return this;
        }

        @Override
        public CommonDao.Criteria limit(int start, int row) {
            limitStr = " limit " + start + "," + row;
            return this;
        }

        public List getParam(){
            return this.param;
        }
        public StringBuilder getCriteriaSQL(){
            return new StringBuilder(criteriaSQL.toString()+limitStr);
        }
    }
}

스프링을 사용한 jdbcTemplate를 실현하였으며, 사실은 가장 원시적인 jdbc로도 실현할 수 있으며, 좀 번거롭지만 의존을 줄일 수 있다.인터페이스 설계가 합리적이라면 실현해 보면 일부 문자열의 결합일 뿐 너무 복잡하지 않을 것이다.이 안에는 맵과 대상이 서로 돌아가는 도구를 사용했습니다. 게으름을 피우기 위해 저는 두 가지 변환 방법을 사용했습니다. 저는fastjson에서 json을 서열화하는 방법으로 변환했습니다.
/**
 * Created by liuruijie on 2017/1/17.
 *     
 */
public class ObjectUtil {
    /**
     *      
     */
    public static Map objectToMap(Object obj){
        return (Map) JSON.toJSON(obj);
    }

    /**
     *      
     */
    public static  T mapToObject(Map map, Class T){
        return (T) JSON.parseObject(JSON.toJSONString(map), T);
    }
}

마지막에 쓴 소감들은 왜 ORM 프레임워크를 사용하지 않고 다오층 인터페이스를 만드는지 간단하고 편리하다는 느낌을 받을 수 있다.이렇게 말하자면 이전에 나는 mybatis를 사용하여 데이터베이스 접근층을 만들었고 그의 플러그인인 mybatisGenerator를 사용하여 데이터베이스에 따라 역방향으로 코드를 생성했다. 이것은 확실히 편리하다는 것을 인정하지 않을 수 없다. 그러나 sql문장을 쓰지 않아도 데이터베이스로 조작할 수 있다.그러나 나중에 모든 테이블은 최소한 포클래스 + xml 파일 하나 + dao 인터페이스 하나 + Example 조건 클래스에 대응하고 업무가 갈수록 복잡해지면서 이런 것들이 점점 많아진다. 그러면 이런 자동으로 생성된 코드는 거의 유지보수할 수 없다는 것을 알게 될 것이다.그리고 이것은 당신을 위해 너무 많은 쓸모없는 코드를 생성했습니다. 코드를 간소화하려면, 반드시 스스로 sql 문장을 써야 합니다.유행하는 프레임워크를 사용하면 코드를 쓰는 것이 더욱 편리할 뿐만 아니라 안정성을 확보할 수 있을 뿐만 아니라 스스로 sql문장을 쓰는 것도 최적화되고 집행 효율을 높일 수 있다. 그러나 얼마나 많은 사람들이 고성능의 sql문장을 쓸 수 있는지 생각해 보세요. 일부 일반적인 업무, 예를 들어 xxx인원관리시스템, xxx도서관리시스템 등, 이런 소형 프로젝트, 고성능의 sql문장이 얼마나 큰 향상을 얻을 수 있는지 생각해 보세요.나의 일관된 사고방식은 우선을 실현한 다음에 확장성과 중용성을 고려한 다음에 안정성을 고려한 다음에 성능 문제를 고려하는 것이다.프로그램이 가장 짧은 시간 안에 실행될 수 있도록 하는 것이야말로 가장 중요한 것이다. 프로그램이 실행될 때의 속도를 높이기 위해 복잡한 실현을 사용하기 때문에 늦게 실행될 수 없다. 마지막으로 코드가 고려하는 요소가 너무 많아서 자신을 어지럽힌다.
여기까지 저는 백엔드 방향에서 웹 프로젝트의 구조 디자인을 전후단의 상호작용부터 업무층의 이상 처리, 데이터 방문층의 디자인까지 모두 자신의 방향을 제시했습니다.비록 완벽하지는 않지만 나 자신에게 이 디자인은 매우 유용하다. 이 세 편의 글에서 언급한 디자인 사고방식은 거의 각종 프로젝트에 활용될 수 있다.

좋은 웹페이지 즐겨찾기