Java鏈接數據庫 #04# Apache Commons DbUtils

DbUtils並不是是什麼ORM框架,只是對原始的JDBC進行了一些封裝,以便咱們少寫一些重複代碼。就「用」而言,僅僅須要學習QueryRunner類和ResultSetHandler接口就能夠了。它的顯著特色就是超級輕量級,總代碼量目測彷佛還不到一萬行。html

經過一個簡單的調用看總體結構

public class TestDbUtils {
    private static final QueryRunner RUNNER = new QueryRunner(HikariCPUtils.getDs());
    public static void main(String[] args) {
        ResultSetHandler<SimpleUser> handler = new BeanHandler<>(SimpleUser.class);
        SimpleUser user = null;
        try {
            user = RUNNER.query("SELECT * FROM simple_user WHERE username=?", handler, "admin123");
        } catch (SQLException e) {
            e.printStackTrace();
        } 
        System.out.println(user);
    }
}
TestDbUtils.java
    /**
     * Calls query after checking the parameters to ensure nothing is null.
     * @param conn The connection to use for the query call.
     * @param closeConn True if the connection should be closed, false otherwise.
     * @param sql The SQL statement to execute.
     * @param params An array of query replacement parameters.  Each row in
     * this array is one set of batch replacement values.
     * @return The results of the query.
     * @throws SQLException If there are database or parameter errors.
     */
    private <T> T query(Connection conn, boolean closeConn, String sql, ResultSetHandler<T> rsh, Object... params)
            throws SQLException {
        if (conn == null) {
            throw new SQLException("Null connection");
        }

        if (sql == null) {
            if (closeConn) {
                close(conn);
            }
            throw new SQLException("Null SQL statement");
        }

        if (rsh == null) {
            if (closeConn) {
                close(conn);
            }
            throw new SQLException("Null ResultSetHandler");
        }

        PreparedStatement stmt = null;
        ResultSet rs = null;
        T result = null;

        try {
            stmt = this.prepareStatement(conn, sql);
            this.fillStatement(stmt, params);
            rs = this.wrap(stmt.executeQuery());
            result = rsh.handle(rs);

        } catch (SQLException e) {
            this.rethrow(e, sql, params);

        } finally {
            try {
                close(rs);
            } finally {
                close(stmt);
                if (closeConn) {
                    close(conn);
                }
            }
        }

        return result;
    }
private T query(Connection conn, boolean closeConn, String sql, ResultSetHandler rsh, Object... params) throws SQLException

AbstractQueryRunner、RowProcessor、ResultSetHandler<T>是Apache Commons DbUtils裏的三大主角,經過繼承上述抽象類/實現上述接口能夠很方便地對API功能進行定製化。java

詳細類圖參考:http://ju.outofmemory.cn/entry/143920git

Examples

官方Examples:http://commons.apache.org/proper/commons-dbutils/examples.htmlgithub

同步請求的代碼在上面已經給出。異步請求的草稿代碼以下:web

public class DbUtilsSampleDAO {

    private AsyncQueryRunner runner = new AsyncQueryRunner(Executors.newCachedThreadPool());

    public Future<Integer> saveProfile(Profile profile) {
        Future<Integer> future;
        Connection conn = null;
        try {
            conn = ConnectionFactory.getConnection();
            future = runner.update(conn,
                    "INSERT ignore INTO `profiles`.`profile` (`username`, `password`, `nickname`) " +
                            "VALUES (?, ?, ?)", profile.getUsername(), profile.getPassword(), profile.getNickname());
        } catch (SQLException e) {
            throw new DaoException(e);
        } finally {
            try {
                DbUtils.close(conn);
            } catch (SQLException e) {
                throw new DaoException(e);
            }
        }
        return future;
    }

    public static void main(String[] args) {
        DbUtilsSampleDAO dao = new DbUtilsSampleDAO();
        Profile profile = new Profile("myusername", "mypassword", "thisnickname");
        Future<Integer> future = dao.saveProfile(profile);
        try {
            System.out.println(future.get() == 1 ? "更新成功" : "更新失敗");
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }
}

修改JAVA鏈接數據庫#03#中的代碼

如下代碼同步更新在GitHub:https://github.com/xkfx/web-test-01sql

主要的幾個類以下:數據庫

① QueryRunnerProxy.javaapache

package org.sample.webapp.db.queryrunner;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.ResultSetHandler;
import org.sample.webapp.db.connmanager.ConnectionFactory;
import org.sample.webapp.exception.DaoException;

import java.sql.SQLException;

/**
 * 封裝DAO層通用代碼,不會關閉connection!!!
 */
public class QueryRunnerProxy {

    private QueryRunnerProxy() {
        // Exists to defeat instantiation
    }

    private static QueryRunner runner = new QueryRunner();

    public static int update(String sql, Object... param) {
        int updates = 0;
        try {
            updates = runner.update(ConnectionFactory.getConnection(), sql, param);
        } catch (SQLException e) {
            throw new DaoException(e);
        }
        return updates;
    }

    public static <T> T query(String sql, ResultSetHandler<T> rsh, Object... params) {
        T result = null;
        try {
            result = runner.query(ConnectionFactory.getConnection(), sql, rsh, params);
        } catch (SQLException e) {
            throw new DaoException(e);
        }
        return result;
    }
}

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -app

② RsHandlers.java框架

package org.sample.webapp.db.queryrunner;

import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.sample.webapp.entity.Profile;

/**
 * 業務相關,保存各式各樣的ResultSetHandler常量
 */
public interface RsHandlers {

    BeanListHandler<Profile> PROFILE_LIST = new BeanListHandler<>(Profile.class);

    BeanHandler<Profile> PROFILE = new BeanHandler<>(Profile.class);
}

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

③ ProfileDAOImpl.java 順便改成enum了,僅是由於以爲這樣寫表示單例更清晰(後來證實這是一個意義很是有限、自尋麻煩的作法。。)

package org.sample.webapp.dao.impl;

import org.sample.webapp.dao.ProfileDAO;
import org.sample.webapp.db.queryrunner.QueryRunnerProxy;
import org.sample.webapp.db.queryrunner.RsHandlers;
import org.sample.webapp.entity.Profile;

import java.util.List;

/**
 * 該類方法統一拋出DaoException
 */
public enum ProfileDAOImpl implements ProfileDAO {

    INSTANCE;

    @Override
    public int saveProfile(Profile profile) {
        final String sql = "INSERT ignore INTO profile (username, password, nickname) " +
                "VALUES (?, ?, ?)"; // 添加ignore出現重複不會拋出異常而是返回0
        return QueryRunnerProxy.update(sql, profile.getUsername(), profile.getPassword(), profile.getNickname());
    }

    @Override
    public List<Profile> listByNickname(String nickname) {
        final String sql = "SELECT  profile_id AS id,  username,  password,  nickname, last_online AS lastOnline, gender, birthday, location, joined " +
                "FROM profile " +
                "WHERE nickname=?";
        return QueryRunnerProxy.query(sql, RsHandlers.PROFILE_LIST, nickname);
    }

    @Override
    public Profile getByUsername(String username) {
        final String sql = "SELECT  profile_id AS id,  username,  password,  nickname, last_online AS lastOnline, gender, birthday, location, joined " +
                "FROM profile " +
                "WHERE username=?"; // TODO 該字符串會反覆建立嗎?
        return QueryRunnerProxy.query(sql, RsHandlers.PROFILE, username);
    }

    @Override
    public int updateById(Profile profile) {
        final String sql = "UPDATE profile " +
                "SET nickname=?, gender=?, birthday=?, location=? " +
                "WHERE profile_id=?";
        return QueryRunnerProxy.update(sql, profile.getNickname(), profile.getGender() != null ? String.valueOf(profile.getGender()) : null,
                profile.getBirthday(), profile.getLocation(), profile.getId());
    }

    @Override
    public int updatePassword(String username, String password) {
        final String sql = "UPDATE profile " +
                "SET password=? " +
                "WHERE username=?";
        return QueryRunnerProxy.update(sql, password, username);
    }

    @Override
    public int updateLastOnline(String username) {
        final String sql = "UPDATE profile " +
                "SET last_online=CURRENT_TIMESTAMP " +
                "WHERE username=?";
        return QueryRunnerProxy.update(sql, username);
    }
}

後續:

一、把sql常量字符串集中放在一個Interface裏。以下:

package org.sample.shop.db.queryrunner;

public interface SQLs {
    // item
    String ITEM_LIST_BY_UID_AND_STATUS = "SELECT id, user_id AS userId, name, price, status, quantity FROM item WHERE user_id=? AND status=?";
    String ITEM_SAVE_ITEM = "INSERT INTO item(user_id, name, price, status, quantity) VALUES (?, ?, ?, ?, ?)";
    String ITEM_REMOVE_BY_ID = "DELETE FROM item WHERE id=?";
    String ITEM_UPDATE_BY_ID = "UPDATE item SET name=?, price=?, status=?, quantity=? WHERE id=?";
    // order
    String ORDER_GET_BY_UID = "SELECT id, user_id AS userId, total FROM simple_order WHERE user_id=?";
    String ORDER_SAVE_ORDER = "INSERT INTO simple_order(user_id, total) VALUES(?, ?)";
    String ORDER_SAVE_ORDER_DETAIL = "INSERT INTO order_detail(order_id, item_id, user_id, quantity, price, status) VALUES(?, ?, ?, ?, ?, ?)";
    // order detail
    String ORDER_DETAIL_GET_BY_ORDER_ID = "SELECT id, order_id AS orderId, item_id AS itemId, user_id AS userId, quantity, price, status FROM order_detail WHERE order_id=?";
    String ORDER_DETAIL_GET_BY_UID = "SELECT id, order_id AS orderId, item_id AS itemId, user_id AS userId, quantity, price, status FROM order_detail WHERE user_id=?";
    // user
    String USER_GET_BY_USERNAME = "SELECT id, username, password, type FROM simple_user WHERE username=?";
    String USER_SAVE_USER = "INSERT INTO simple_user(type, username, password) VALUES (?, ?, ?)";
    // transport order
    String TRANSPORT_ORDER_SAVE_ORDER = "INSERT INTO transport_order(user_id, order_detail_id, location, status) VALUES (?, ?, ?, ?)";
    String TRANSPORT_ORDER_GET_BY_UID = "SELECT id, user_id AS userId, order_detail_id AS detailId, location, status FROM transport_order WHERE user_id=?";
    String TRANSPORT_ORDER_GET_BY_DETAIL_ID = "SELECT id, user_id AS userId, order_detail_id AS detailId, location, status FROM transport_order WHERE order_detail_id=?";
    String TRANSPORT_ORDER_UPDATE_BY_ID = "UPDATE transport_order SET location=?, status=? WHERE id=?";
}
相關文章
相關標籤/搜索