JDBC PreparedStatement 的命名参数实现

Named Parameters for PreparedStatement | InfoWorld

PreparedStatement 语法问题

PreparedStatement 的问题源于其参数语法。参数是匿名的,并通过索引访问,如下所示:

PreparedStatement p = con.prepareStatement("select * from people where 
(first_name = ? or last_name = ?) and address = ?");
p.setString(1, name);
p.setString(2, name);
p.setString(3, address);

对于带有一两个参数的小型查询,这不是问题。但是,对于较大的查询,跟踪索引变得非常困难。开发人员必须仔细阅读 SQL 语句并计算问号以确定参数插入的位置。如果删除或插入参数,则必须重新编号所有后续参数索引。很明显,如果参数接近语句的开头并且有多个参数,或者如果查询被重组以使参数以不同的顺序出现,这可能会出现问题。

另一个不便之处是设置多个在逻辑上可能相同的参数。这可能发生在诸如select * from people where first_name=? or last_name=?. (这个特定的查询可以重写为select * from people where ? in (first_name, last_name),但有些查询不那么容易转换。)

解决方法

避免重新编号索引的一种解决方法是使用计数器来跟踪索引:

PreparedStatement p = con.prepareStatement("select * from people where 
(first_name = ? or last_name = ?) and address = ?");
int i = 1;
p.setString(i++, name);
p.setString(i++, name);
p.setString(i++, address);

这对于插入和删除参数时的稳定性特别有效。但是,代码仍然不再具有可读性,程序员仍然必须注意确保参数的列出顺序与它们在查询中使用的顺序相同。

命名参数语句

这将我们带到了我的班级(因为没有更好的名字)NamedParameterStatement。语法与 PreparedStatement 相同,只是参数表示为冒号后跟标识符,而不是问号。

String query = "select * from people where (first_name = :name or last_name 
= :name) and address = :address");
NamedParameterStatement p = new NamedParameterStatement(con, query);
p.setString("name", name);
p.setString("address", address);

在幕后,该类通过用问号替换参数标记并创建 PreparedStatement 来工作。在参数名称和它们的索引之间保留一个映射。注入参数时会引用此映射。这两个类是相互兼容的,因此程序员可以在他们认为合适的情况下将 PreparedStatement 用于某些查询,并将 NamedParameterStatement 用于其他查询。

表现

与执行查询所需的时间相比,翻译查询和查找参数索引所花费的时间实际上是最少的。如果需要考虑翻译时间,则可以将类修改为具有attach(Connection)detach()方法。NamedParameterStatement 可以预先创建、附加到连接、使用和分离。但是,缓存(和同步,如有必要)这些对象所花费的时间可能会超过创建新对象所需的时间。

在我的计算机上针对一张小表对上述查询进行的测试给出了 NamedParameterStatement 的 352 微秒、AttachableNamedParameterStatement(无同步)的 325 微秒和原始 PreparedStatement 的 332 微秒的时间。翻译一个查询大约需要 6 微秒。不可思议的是,AttachableNamedParameterStatement 始终比原始 PreparedStatement 稍有优势。由于垃圾收集、即时编译等原因,对 Java 代码进行基准测试是出了名的困难,因此对这些结果应该持保留态度。

一般来说,性能不是问题,尤其是当实际查询正在做任何重要的事情时。

结论

NamedParameterStatement 作为 PreparedStatement 的直接替代品是有效的,无需配置。新类提供的更简单的界面可以提高程序员的工作效率。也许更重要的是,维护更容易,因为代码更具可读性。

代码

//STEP 1. Import required packages

import java.sql.*;

public class JDBCNamedParameterStatement {
    // JDBC driver name and database URL

    static final String DB_DRIVER_CLASS_NAME = "com.mysql.cj.jdbc.Driver";
    static final String DB_URL = "jdbc:mysql://localhost:3306/emp?autoReconnect=true&useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai";

    //  Database credentials
    static final String DB_USERNAME = "root";
    static final String DB_PASSWORD = "root";

    public static void main(String[] args) {
        Connection conn = null;
        try {
            //STEP 2: Register JDBC driver
            Class.forName(DB_DRIVER_CLASS_NAME);

            //STEP 3: Open a connection
            System.out.println("Connecting to database...");
            conn = DriverManager.getConnection(DB_URL, DB_USERNAME, DB_PASSWORD);

            //STEP 4: Execute a query
            System.out.println("Creating statement...");

            String query = "SELECT id, first, last, age FROM Employees where id=:id";
            NamedParameterStatement p = new NamedParameterStatement(conn, query);
            p.setInt("id", 100);
            ResultSet rs = p.executeQuery();

            //STEP 5: Extract data from result set
            while (rs.next()) {
                //Retrieve by column name
                int id = rs.getInt("id");
                int age = rs.getInt("age");
                String first = rs.getString("first");
                String last = rs.getString("last");

                //Display values
                System.out.print("ID: " + id);
                System.out.print(", Age: " + age);
                System.out.print(", First: " + first);
                System.out.println(", Last: " + last);
            }
            //STEP 6: Clean-up environment
            rs.close();
            p.close();
            conn.close();
        } catch (SQLException se) {
            //Handle errors for JDBC
            se.printStackTrace();
        } catch (Exception e) {
            //Handle errors for Class.forName
            e.printStackTrace();
        } finally {
            //finally block used to close resources
            try {
                if (conn != null) {
                    conn.close();
                }
            } catch (SQLException se) {
                se.printStackTrace();
            }//end finally try
        }//end try
        System.out.println("Goodbye!");
    }//end main
}//end JDBCExample


import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 * This class wraps around a {@link PreparedStatement} and allows the programmer
 * to set parameters by name instead of by index. This eliminates any confusion
 * as to which parameter index represents what. This also means that rearranging
 * the SQL statement or adding a parameter doesn't involve renumbering your
 * indices. Code such as this:
 *
 *
 * Connection con=getConnection(); String query="select * from my_table where
 * name=? or address=?"; PreparedStatement p=con.prepareStatement(query);
 * p.setString(1, "bob"); p.setString(2, "123 terrace ct"); ResultSet
 * rs=p.executeQuery();
 *
 * can be replaced with:
 *
 * Connection con=getConnection(); String query="select * from my_table where
 * name=:name or address=:address"; NamedParameterStatement p=new
 * NamedParameterStatement(con, query); p.setString("name", "bob");
 * p.setString("address", "123 terrace ct"); ResultSet rs=p.executeQuery();
 *
 * Sourced from JavaWorld Article @
 * http://www.javaworld.com/javaworld/jw-04-2007/jw-04-jdbc.html
 *
 * @author adam_crume
 */
public class NamedParameterStatement {

    /**
     * The statement this object is wrapping.
     */
    private final PreparedStatement statement;

    /**
     * Maps parameter names to arrays of ints which are the parameter indices.
     */
    private Map<String, int[]> indexMap;

    /**
     * Creates a NamedParameterStatement. Wraps a call to c.{@link Connection#prepareStatement(java.lang.String)
     * prepareStatement}.
     *
     * @param connection the database connection
     * @param query the parameterized query
     * @throws SQLException if the statement could not be created
     */
    public NamedParameterStatement(Connection connection, String query) throws SQLException {
        String parsedQuery = parse(query);
        statement = connection.prepareStatement(parsedQuery);
    }

    /**
     * Parses a query with named parameters. The parameter-index mappings are
     * put into the map, and the parsed query is returned. DO NOT CALL FROM
     * CLIENT CODE. This method is non-private so JUnit code can test it.
     *
     * @param query query to parse
     * @param paramMap map to hold parameter-index mappings
     * @return the parsed query
     */
    final String parse(String query) {
        // I was originally using regular expressions, but they didn't work well for ignoring
        // parameter-like strings inside quotes.
        int length = query.length();
        StringBuffer parsedQuery = new StringBuffer(length);
        boolean inSingleQuote = false;
        boolean inDoubleQuote = false;
        int index = 1;
        HashMap<String, List<Integer>> indexes = new HashMap<String, List<Integer>>(10);

        for (int i = 0; i < length; i++) {
            char c = query.charAt(i);
            if (inSingleQuote) {
                if (c == '\'') {
                    inSingleQuote = false;
                }
            } else if (inDoubleQuote) {
                if (c == '"') {
                    inDoubleQuote = false;
                }
            } else {
                if (c == '\'') {
                    inSingleQuote = true;
                } else if (c == '"') {
                    inDoubleQuote = true;
                } else if (c == ':' && i + 1 < length && Character.isJavaIdentifierStart(query.charAt(i + 1))) {
                    int j = i + 2;
                    while (j < length && Character.isJavaIdentifierPart(query.charAt(j))) {
                        j++;
                    }
                    String name = query.substring(i + 1, j);
                    c = '?'; // replace the parameter with a question mark
                    i += name.length(); // skip past the end if the parameter

                    List<Integer> indexList = indexes.get(name);
                    if (indexList == null) {
                        indexList = new LinkedList<Integer>();
                        indexes.put(name, indexList);
                    }
                    indexList.add(Integer.valueOf(index));

                    index++;
                }
            }
            parsedQuery.append(c);
        }

        indexMap = new HashMap<String, int[]>(indexes.size());
        // replace the lists of Integer objects with arrays of ints
        for (Map.Entry<String, List<Integer>> entry : indexes.entrySet()) {
            List<Integer> list = entry.getValue();
            int[] intIndexes = new int[list.size()];
            int i = 0;
            for (Integer x : list) {
                intIndexes[i++] = x.intValue();
            }
            indexMap.put(entry.getKey(), intIndexes);
        }

        return parsedQuery.toString();
    }

    /**
     * Returns the indexes for a parameter.
     *
     * @param name parameter name
     * @return parameter indexes
     * @throws IllegalArgumentException if the parameter does not exist
     */
    private int[] getIndexes(String name) {
        int[] indexes = indexMap.get(name);
        if (indexes == null) {
            throw new IllegalArgumentException("Parameter not found: " + name);
        }
        return indexes;
    }

    /**
     * Sets a parameter.
     *
     * @param name parameter name
     * @param value parameter value
     * @throws SQLException if an error occurred
     * @throws IllegalArgumentException if the parameter does not exist
     * @see PreparedStatement#setObject(int, java.lang.Object)
     */
    public void setObject(String name, Object value) throws SQLException {
        int[] indexes = getIndexes(name);
        for (int i = 0; i < indexes.length; i++) {
            statement.setObject(indexes[i], value);
        }
    }

    /**
     * Sets a parameter.
     *
     * @param name parameter name
     * @param value parameter value
     * @throws SQLException if an error occurred
     * @throws IllegalArgumentException if the parameter does not exist
     * @see PreparedStatement#setString(int, java.lang.String)
     */
    public void setString(String name, String value) throws SQLException {
        int[] indexes = getIndexes(name);
        for (int i = 0; i < indexes.length; i++) {
            statement.setString(indexes[i], value);
        }
    }

    /**
     * Sets a parameter.
     *
     * @param name parameter name
     * @param value parameter value
     * @throws SQLException if an error occurred
     * @throws IllegalArgumentException if the parameter does not exist
     * @see PreparedStatement#setInt(int, int)
     */
    public void setInt(String name, int value) throws SQLException {
        int[] indexes = getIndexes(name);
        for (int i = 0; i < indexes.length; i++) {
            statement.setInt(indexes[i], value);
        }
    }

    /**
     * Sets a parameter.
     *
     * @param name parameter name
     * @param value parameter value
     * @throws SQLException if an error occurred
     * @throws IllegalArgumentException if the parameter does not exist
     * @see PreparedStatement#setInt(int, int)
     */
    public void setLong(String name, long value) throws SQLException {
        int[] indexes = getIndexes(name);
        for (int i = 0; i < indexes.length; i++) {
            statement.setLong(indexes[i], value);
        }
    }

    /**
     * Sets a parameter.
     *
     * @param name parameter name
     * @param value parameter value
     * @throws SQLException if an error occurred
     * @throws IllegalArgumentException if the parameter does not exist
     * @see PreparedStatement#setTimestamp(int, java.sql.Timestamp)
     */
    public void setTimestamp(String name, Timestamp value) throws SQLException {
        int[] indexes = getIndexes(name);
        for (int i = 0; i < indexes.length; i++) {
            statement.setTimestamp(indexes[i], value);
        }
    }

    /**
     * Returns the underlying statement.
     *
     * @return the statement
     */
    public PreparedStatement getStatement() {
        return statement;
    }

    /**
     * Executes the statement.
     *
     * @return true if the first result is a {@link ResultSet}
     * @throws SQLException if an error occurred
     * @see PreparedStatement#execute()
     */
    public boolean execute() throws SQLException {
        return statement.execute();
    }

    /**
     * Executes the statement, which must be a query.
     *
     * @return the query results
     * @throws SQLException if an error occurred
     * @see PreparedStatement#executeQuery()
     */
    public ResultSet executeQuery() throws SQLException {
        return statement.executeQuery();
    }

    /**
     * Executes the statement, which must be an SQL INSERT, UPDATE or DELETE
     * statement; or an SQL statement that returns nothing, such as a DDL
     * statement.
     *
     * @return number of rows affected
     * @throws SQLException if an error occurred
     * @see PreparedStatement#executeUpdate()
     */
    public int executeUpdate() throws SQLException {
        return statement.executeUpdate();
    }

    /**
     * Closes the statement.
     *
     * @throws SQLException if an error occurred
     * @see Statement#close()
     */
    public void close() throws SQLException {
        statement.close();
    }

    /**
     * Adds the current set of parameters as a batch entry.
     *
     * @throws SQLException if something went wrong
     */
    public void addBatch() throws SQLException {
        statement.addBatch();
    }

    /**
     * Executes all of the batched statements.
     *
     * See {@link Statement#executeBatch()} for details.
     *
     * @return update counts for each statement
     * @throws SQLException if something went wrong
     */
    public int[] executeBatch() throws SQLException {
        return statement.executeBatch();
    }
}

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

public class NamedParameterStatement implements AutoCloseable {

    public static final int[] EMPTY = {};
    /**
     * The statement this object is wrapping.
     */
    public final PreparedStatement statement;

    /**
     * Maps parameter names to arrays of ints which are the parameter indices.
     */
    private final Map indexMap;

    /**
     * Creates a NamedParameterStatement. Wraps a call to      c.{@link Connection#prepareStatement(java.lang.String)
     * prepareStatement}.
     *
     * @param connection the database connection
     * @param query the parameterized query
     * @throws SQLException if the statement could not be created
     */
    public NamedParameterStatement(Connection connection, String query) throws SQLException {
        indexMap = new HashMap();
        String parsedQuery = parse(query, indexMap);
        statement = connection.prepareStatement(parsedQuery);
    }

    /**
     * Parses a query with named parameters. The parameter-index mappings are
     * put into the map, and the parsed query is returned. DO NOT CALL FROM
     * CLIENT CODE. This method is non-private so JUnit code can test it.
     *
     * @param query query to parse
     * @param paramMap map to hold parameter-index mappings
     * @return the parsed query
     */
    static String parse(String query, Map paramMap) {
        // I was originally using regular expressions, but they didn't work well for ignoring
        // parameter-like strings inside quotes.
        int length = query.length();
        final StringBuilder parsedQuery = new StringBuilder(length);
        boolean inSingleQuote = false;
        boolean inDoubleQuote = false;
        int index = 1;

        for (int i = 0; i < length; i++) {
            char c = query.charAt(i);
            if (inSingleQuote) {
                if (c == '\'') {
                    inSingleQuote = false;
                }
            } else if (inDoubleQuote) {
                if (c == '"') {
                    inDoubleQuote = false;
                }
            } else {
                if (c == '\'') {
                    inSingleQuote = true;
                } else if (c == '"') {
                    inDoubleQuote = true;
                } else if (c == ':' && i + 1 < length
                        && Character.isJavaIdentifierStart(query.charAt(i + 1))) {
                    int j = i + 2;
                    while (j < length && Character.isJavaIdentifierPart(query.charAt(j))) {
                        j++;
                    }
                    String name = query.substring(i + 1, j);
                    c = '?'; // replace the parameter with a question mark
                    i += name.length(); // skip past the end if the parameter

                    List indexList = (List) paramMap.get(name);
                    if (indexList == null) {
                        indexList = new LinkedList();
                        paramMap.put(name, indexList);
                    }
                    indexList.add(new Integer(index));

                    index++;
                }
            }
            parsedQuery.append(c);
        }

        // replace the lists of Integer objects with arrays of ints
        for (Iterator itr = paramMap.entrySet().iterator(); itr.hasNext();) {
            Map.Entry entry = (Map.Entry) itr.next();
            List list = (List) entry.getValue();
            int[] indexes = new int[list.size()];
            int i = 0;
            for (Object aList : list) {
                Integer x = (Integer) aList;
                indexes[i++] = x.intValue();
            }
            list.clear();
            entry.setValue(indexes);
        }

        return parsedQuery.toString();
    }

    /**
     * Returns the indexes for a parameter.
     *
     * @param name parameter name
     * @return parameter indexes
     * @throws IllegalArgumentException if the parameter does not exist
     */
    private int[] getIndexes(String name) {
        int[] indexes = (int[]) indexMap.get(name);
        if (indexes == null) {
//            throw new IllegalArgumentException("Parameter not found: " + name);
            return EMPTY;
        }
        return indexes;
    }

    /**
     * Sets a parameter.
     *
     * @param name parameter name
     * @param value parameter value
     * @throws SQLException if an error occurred
     * @throws IllegalArgumentException if the parameter does not exist
     * @see PreparedStatement#setObject(int, java.lang.Object)
     */
    public void setObject(String name, Object value) throws SQLException {
        int[] indexes = getIndexes(name);
        for (int i = 0; i < indexes.length; i++) {
            statement.setObject(indexes[i], value);
        }
    }

    public void setProperties(Map<String, Object> params) throws SQLException {
        for (Map.Entry<String, Object> entry : params.entrySet()) {
            int[] indexes = getIndexes(entry.getKey());
            final Object value = entry.getValue();
            Timestamp valueTS = null;
            if (value != null && value.getClass() == Date.class) {
                valueTS = new Timestamp(((Date) value).getTime());
            }
            for (int i : indexes) {
                if (valueTS != null) {
                    statement.setTimestamp(i, valueTS);
                } else {
                    statement.setObject(i, value);
                }
            }
        }
    }

    /**
     * Sets a parameter.
     *
     * @param name parameter name
     * @param value parameter value
     * @throws SQLException if an error occurred
     * @throws IllegalArgumentException if the parameter does not exist
     * @see PreparedStatement#setString(int, java.lang.String)
     */
    public void setString(String name, String value) throws SQLException {
        int[] indexes = getIndexes(name);
        for (int i = 0; i < indexes.length; i++) {
            statement.setString(indexes[i], value);
        }
    }

    /**
     * Sets a parameter.
     *
     * @param name parameter name
     * @param value parameter value
     * @throws SQLException if an error occurred
     * @throws IllegalArgumentException if the parameter does not exist
     * @see PreparedStatement#setInt(int, int)
     */
    public void setInt(String name, int value) throws SQLException {
        int[] indexes = getIndexes(name);
        for (int i = 0; i < indexes.length; i++) {
            statement.setInt(indexes[i], value);
        }
    }

    /**
     * Sets a parameter.
     *
     * @param name parameter name
     * @param value parameter value
     * @throws SQLException if an error occurred
     * @throws IllegalArgumentException if the parameter does not exist
     * @see PreparedStatement#setInt(int, int)
     */
    public void setLong(String name, long value) throws SQLException {
        int[] indexes = getIndexes(name);
        for (int i = 0; i < indexes.length; i++) {
            statement.setLong(indexes[i], value);
        }
    }

    public void setDouble(String name, double value) throws SQLException {
        int[] indexes = getIndexes(name);
        for (int i = 0; i < indexes.length; i++) {
            statement.setDouble(indexes[i], value);
        }
    }

    /**
     * Sets a parameter.
     *
     * @param name parameter name
     * @param value parameter value
     * @throws SQLException if an error occurred
     * @throws IllegalArgumentException if the parameter does not exist
     * @see PreparedStatement#setTimestamp(int, java.sql.Timestamp)
     */
    public void setTimestamp(String name, Timestamp value) throws SQLException {
        int[] indexes = getIndexes(name);
        for (int i = 0; i < indexes.length; i++) {
            statement.setTimestamp(indexes[i], value);
        }
    }

    /**
     * Returns the underlying statement.
     *
     * @return the statement
     */
    public PreparedStatement getStatement() {
        return statement;
    }

    /**
     * Executes the statement.
     *
     * @return true if the first result is a {@link ResultSet}
     * @throws SQLException if an error occurred
     * @see PreparedStatement#execute()
     */
    public boolean execute() throws SQLException {
        return statement.execute();
    }

    /**
     * Executes the statement, which must be a query.
     *
     * @return the query results
     * @throws SQLException if an error occurred
     * @see PreparedStatement#executeQuery()
     */
    public ResultSet executeQuery() throws SQLException {
        return statement.executeQuery();
    }

    /**
     * Executes the statement, which must be an SQL INSERT, UPDATE or DELETE
     * statement; or an SQL statement that returns nothing, such as a DDL
     * statement.
     *
     * @return number of rows affected
     * @throws SQLException if an error occurred
     * @see PreparedStatement#executeUpdate()
     */
    public int executeUpdate() throws SQLException {
        return statement.executeUpdate();
    }

    /**
     * Closes the statement.
     *
     * @throws SQLException if an error occurred
     * @see Statement#close()
     */
    public void close() throws SQLException {
        statement.close();
        indexMap.clear();
    }

    /**
     * Adds the current set of parameters as a batch entry.
     *
     * @throws SQLException if something went wrong
     */
    public void addBatch() throws SQLException {
        statement.addBatch();
    }

    /**
     * Executes all of the batched statements.
     * <p>
     * See {@link Statement#executeBatch()} for details.
     *
     * @return update counts for each statement
     * @throws SQLException if something went wrong
     */
    public int[] executeBatch() throws SQLException {
        return statement.executeBatch();
    }

    public void setFetchSize(int rows) throws SQLException {
        statement.setFetchSize(rows);
        statement.setFetchDirection(ResultSet.FETCH_FORWARD);
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1、JDBC(Java Database Connection):java连接数据库统一接口API,底层主要通过直接的JDBC驱动和 JDBC-ODBC桥驱动实现数据库的连接。 1>.JDBC驱动程序类型: <1>.JDBC-ODBC桥加ODBC驱动程序:需要ODBC驱动,适合于企业网或三层结构应用程序 <2>.本地API:需要驱动程序的二进制代码支持 <3>.JDBC网络纯java驱动程序:将JDBC转换为与DBMS无关的网络协议,又被某服务器转换为一种DBMS 协议,以操作各种数据库 <4>.本地协议纯java驱动程序:将JDBC调用直接转换成JDBC所使用的网络协议 2、JDBC操作基本流程: 1>.导入驱动:实例化时自动向DriverManager注册(DriverManager.registerDriver()) <1>.Class.forName(driver) <2>.Class.forName(driver).newInstance() <3>.new driver() 2>.取得数据库连接(Connect to the DataBase) <1>.用DriverManager取数据库连接 Connection cn = DriverManager.getConnection(url,uid,pwd); <2>.用jndi(java命名和目录服务)方式:多用于jsp Context ctx = (Context) new InitialContext().lookup("java:comp/env"); DataSource ds = (DataSource) ctx.lookup(jndi); Connection cn = ds.getConnection(); 3>.执行sql语句(Execute the SQL) <1>.用Statement来执行sql语句 Statement sm = cn.createStatement(); sm.executeQuery(sql); // 执行数据查询语句(select) sm.executeUpdate(sql); // 执行数据更新语句(delete、update、insert、drop等) <2>.用PreparedStatement来执行sql语句 String sql = "insert into user (id,name) values (?,?)"; PreparedStatement ps = cn.prepareStatement(sql); ps.setInt(1,xxx); ps.setString(2,xxx); ... ResultSet rs = ps.executeQuery(); // 查询 int c = ps.executeUpdate(); // 更新 4>.处理执行结果: <1>.查询语句,返回记录集ResultSet <2>.更新语句,返回数字,表示该更新影响的记录数 <3>.ResultSet的方法:while(re.next()) next(),将游标往后移动一行,如果成功返回true;否则返回false getInt("id")或getSting("name"),返回当前游标下某个字段的值 5>.释放数据库连接 rs.close(); ps.close(); /stat.close(); con.close(); 3、创建可滚动、更新的记录集 1>.创建Statement时指定参数:该Statement取得的ResultSet就是可滚动的 Statement sm = cn.createStatement(ResultSet.TYPE_SCROLL_ENSITIVE, ResultSet.CONCUR_READ_ONLY); 2>.创建PreparedStatement时指定参数 PreparedStatemet ps = cn.prepareStatement(sql,ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); rs.absolute(9000);
1.1 Java语言发展简史2 1.2 认识Java语言3 1.2.1 Java语言特性3 1.2.2 JavaApplet4 1.2.3 丰富的类库4 1.2.4 Java的竞争对手5 1.2.5 Java在应用领域的优势7 1.3 Java平台的体系结构7 1.3.1 JavaSE标准版8 1.3.2 JavaEE企业版10 1.3.3 JavaME微型版11 1.4 JavaSE环境安装和配置12 1.4.1 什么是JDK12 1.4.2 JDK安装目录和实用命令工具介绍12 1.4.3 设置环境变量13 1.4.4 验证配置的正确性14 1.5 MyEcilpse工具介绍JavaSE环境安装和配置15 1.6 本章练习16 第2章 2.1 什么是程序18 2.2 计算机中的程序18 2.3 Java程序19 2.3.1 Java程序中的类型19 2.3.2 Java程序开发三步曲21 2.3.3 开发Java第一个程序21 2.3.4 Java代码中的注释23 2.3.5 常见错误解析24 2.4 Java类库组织结构和文档27 2.5 Java虚拟机简介28 2.6 Java技术两种核心运行机制29 2.7 上机练习30 第3章 3.1 变量32 3.1.1 什么是变量32 3.1.2 为什么需要变量32 3.1.3 变量的声明和赋值33 3.1.4 变量应用实例33 3.2 数据的分类34 3.2.1 Java中的八种基本数据类型34 3.2.2 普及二进制36 3.2.3 进制间转换37 3.2.4 基本数据类型间转换38 3.2.5 数据类型应用实例38 3.2.6 引用数据类型39 3.3 关键字.标识符.常量39 3.3.1 变量命名规范39 3.3.2 经验之谈-常见错误的分析与处理40 3.3.3 Java标识符命名规则41 3.3.4 关键字42 3.3.5 常量42 3.4 运算符43 3.4.1 算术运算符43 3.4.2 赋值操作符45 3.4.3 关系操作符47 3.4.4 逻辑操作符48 3.4.5 位操作符49 3.4.6 移位运算符49 3.4.7 其他操作符50 3.5 表达式52 3.5.1 表达式简介52 3.5.2 表达式的类型和值52 3.5.3 表达式的运算顺序52 3.5.4 优先级和结合性问题52 3.6 选择结构54 3.6.1 顺序语句54 3.6.2 选择条件语句54 3.6.3 switch结构59 3.6.4 经验之谈-常见错误的分析与处理65 3.6.5 Switch和多重if结构比较66 3.7 循环语句66 3.7.1 While循环67 3.7.2 经验之谈-常见while错误70 3.7.3 do-while循环72 3.7.4 for循环74 3.7.5 经验之谈-for常见错误76 3.7.6 循环语句小结78 3.7.7 break语句79 3.7.8 continue语句82 3.8 JavaDebug技术84 3.9 本章练习85 第4章 4.1 一维数组90 4.1.1 为什么要使用数组90 4.1.2 什么是数组91 4.1.3 如何使用数组92 4.1.4 经验之谈-数组常见错误97 4.2 常用算法98 4.2.1 平均值,最大值,最小值98 4.2.3 数组排序102 4.2.3 数组复制103 4.3 多维数组105 4.3.1 二重循环105 4.3.2 控制流程进阶107 4.3.3 二维数组111 4.4 经典算法113 4.4.1 算法-冒泡排序113 4.4.2 插入排序115 4.5 增强for循环116 4.6 本章练习117 第5章 5.1 面向过程的设计思想120 5.2 面向对象的设计思想120 5.3 抽象121 5.3.1 对象的理解121 5.3.2 Java抽象思想的实现122 5.4 封装124 5.4.1 对象封装的概念理解124 5.4.2 类的理解125 5.4.3 Java类模板创建125 5.4.4 Java中对象的创建和使用127 5.5 属性130 5.5.1 属性的定义130 5.5.2 变量131 5.6 方法132 5.6.1 方法的定义132 5.6.2 构造方法135 5.6.4 方法重载138 5.6.5 自定义方法138 5.6.6 系统提供方法139 5.6.7 方法调用140 5.6.8 方法参数及其传递问题144 5.6.9 理解main方法语法及命令行参数147 5.6.1 0递归算法147 5.7 this关键字148 5.8 JavaBean149 5.9 包150 5.9.1 为什么需要包?150 5.9.2 如何创建包151 5.9.3 编译并生成包:151
ibatis 开发指南ibatis Quick Start............................................................................................ 5 准备工作.......................................................................................................... 5 构建ibatis 基础代码.................................................................................... 5 ibatis 配置........................................................................................................... 11 ibatis 基础语义...................................................................................................... 16 XmlSqlMapClientBuilder................................................................... 16 SqlMapClient ........................................................................................... 16 SqlMapClient 基本操作示例.......................................................... 16 OR 映射........................................................................................................... 19 ibatis 高级特性...................................................................................................... 26 数据关联........................................................................................................ 26 一对多关联............................................................................................ 26 一对一关联............................................................................................ 28 延迟加载........................................................................................................ 30 动态映射........................................................................................................ 31 事务管理........................................................................................................ 35 基于JDBC 的事务管理机制................................................................ 35 基于JTA 的事务管理机制................................................................... 36 外部事务管理......................................................................................... 38 Cache .............................................................................................................. 39 MEMORY 类型Cache 与WeakReference ........................................ 40 LRU 型Cache ....................................................................................... 42 FIFO 型Cache ...................................................................................... 43 OSCache................................................................................................. 43 ibatis 开发指南相对Hibernate 和Apache OJB 等“一站式”ORM 解决方案而言,ibatis 是一种“半自动化”的ORM 实现。所谓“半自动”,可能理解上有点生涩。纵观目前主流的ORM ,无论Hibernate 还是Apache OJB,都对数据库结构提供了较为完整的封装,提供了从POJO 到数据库表的全套映射机制。程序员往往只需定义好了POJO 到数据库表的映射关系,即可通过Hibernate 或者OJB 提供的方法完成持久层操作。程序员甚至不需要对SQL 的熟练掌握, Hibernate/OJB 会根据制定的存储逻辑,自动生成对应的SQL 并调用JDBC 接口加以执行。大多数情况下( 特别是对新项目,新系统的开发而言),这样的机制无往不利,大有一统天下的势头。但是,在一些特定的环境下,这种一站式的解决方案却未必灵光。在笔者的系统咨询工作过程中,常常遇到以下情况: 1. 系统的部分或全部数据来自现有数据库,处于安全考虑,只对开发团队提供几条Select SQL(或存储过程)以获取所需数据,具体的表结构不予公开。2. 开发规范中要求, 所有牵涉到业务逻辑部分的数据库操作,必须在数据库层由存储过程实现(就笔者工作所面向的金融行业而言,工商银行、中国银行、交通银行,都在开发规范中严格指定)3. 系统数据处理量巨大,性能要求极为苛刻,这往往意味着我们必须通过经过高度优化的SQL 语句(或存储过程)才能达到系统性能设计指标。面对这样的需求,再次举起Hibernate 大刀,却发现刀锋不再锐利,甚至无法使用,奈何?恍惚之际,只好再摸出JDBC 准备拼死一搏……,说得未免有些凄凉,直接使用JDBC 进行数据库操作实际上也是不错的选择,只是拖沓的数据库访问代码,乏味的字段读取操作令人厌烦。“半自动化”的ibatis,却刚好解决了这个问题。这里的“半自动化”,是相对Hibernate 等提供了全面的数据库封装机制的“全自动化”ORM 实现而言,“全自动”ORM 实现了POJO 和数据库表之间的映射,以及SQL 的自动生成和执行。而ibatis 的着力点,则在于POJO 与SQL 之间的映射关系。也就是说,ibatis 并不会为程序员在运行期自动生成SQL 执行。具体的SQL 需要程序员编写,然后通过映射配置文件,将SQL 所需的参数,以及返回的结果字段映射到指定POJO 。使用ibatis 提供的ORM 机制,对业务逻辑实现人员而言,面对的是纯粹的Java 对象, 这一层与通过Hibernate 实现ORM 而言基本一致,而对于具体的数据操作,Hibernate 会自动生成SQL 语句,而ibatis 则要求开发者编写具体的SQL 语句。相对Hibernate 等“全自动”ORM 机制而言,ibatis 以SQL 开发的工作量和数据库移植性上的让步,为系统设计提供了更大的自由空间。作为“全自动”ORM 实现的一种有益补充,ibatis 的出现显得别具意义。ibatis Quick Start 准备工作1. 下载ibatis 软件包(http://www.ibatis.com)。2. 创建测试数据库,并在数据库中创建一个t_user 表,其中包含三个字段: . id(int) . name(varchar) . sex(int) 。3. 为了在开发过程更加直观,我们需要将ibatis 日志打开以便观察ibatis 运作的细节。ibatis 采用Apache common_logging,并结合Apache log4j 作为日志输出组件。在CLASSPATH 中新建log4j.properties 配置文件,内容如下:log4j.rootLogger=DEBUG, stdout log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%c{1} -%m%n log4j.logger.java.sql.PreparedStatement=DEBUG 构建ibatis 基础代码ibatis 基础代码包括: 1. ibatis 实例配置一个典型的配置文件如下(具体配置项目的含义见后): <!DOCTYPE sqlMapConfigPUBLIC "-//iBATIS.com//DTD SQL Map Config 2.0//EN""http://www.ibatis.com/dtd/sql-map-config-2.dtd"><settingscacheModelsEnabled="true"enhancementEnabled="true"lazyLoadingEnabled="true"errorTracingEnabled="true"maxRequests="32"maxSessions="10"maxTransactions="5"useStatementNamespaces="false" /> <property name="JDBC.Driver" value="com.p6spy.engine.spy.P6SpyDriver"/> <property name="JDBC.ConnectionURL" value="jdbc:mysql://localhost/sample"/> <property name=value="10"/> <property name=value="120000"/> <property name="Pool.PingQuery" ="select 1 from ACCOUNT"/> <property name=value="1"/> <property name=value="1"/> valuevalue"Pool.MaximumActiveConnections" "Pool.MaximumIdleConnections" "Pool.MaximumCheckoutTime" valuevaluevalue"Pool.PingConnectionsOlderThan" "Pool.PingConnectionsNotUsedFor" 2. POJO(Plain Ordinary Java Object) 下面是我们用作示例的一个POJO: public class User implements Serializable { private Integer id; private String name; private Integer sex; private Set addresses = new HashSet(); /** default constructor */public User() { } public Integer getId() {return this.id; } public void setId(Integer id) {this.id = id; } public String getName() {return this.name; } public void setName(String name) {this.name = name; } public Integer getSex() {return this.sex; } public void setSex(Integer sex) {this.sex = sex; } }3. 映射文件与Hibernate 不同。因为需要人工编写SQL 代码,ibatis 的映射文件一般采用手动编写(通过Copy/Paste,手工编写映射文件也并没想象中的麻烦)。针对上面POJO 的映射代码如下:<!DOCTYPE sqlMapPUBLIC "-//iBATIS.com//DTD SQL Map 2.0//EN""http://www.ibatis.com/dtd/sql-map-2.dtd"><select id="getUser"parameterClass="java.lang.String"resultClass="user"><![CDATA[select name, sex from t_user where name = #name# ]]><update id="updateUser"parameterClass="user"> <![CDATA[UPDATE t_user SETname=#name#, sex=#sex# WHERE id = #id# ]]><insert id="insertUser"parameterClass="user">INSERT INTO t_user ( name, sex)VALUES ( #name#,#sex# ) <delete id="deleteUser"parameterClass="java.lang.String">delete from t_user where id = #value# 从上面的映射文件可以看出,通过、、、四个节点,我们分别定义了针对TUser 对象的增删改查操作。在这四个节点中,我们指定了对应的SQL 语句,以update 节点为例: ……<update id="updateUser" ⑴ parameterClass="user"> ⑵ <![CDATA[ ⑶ UPDATE t_user ⑷ SET ( IBATIS Developer’s Guide Version 1.0 name=#name#, ⑸ sex=#sex# ⑹ ) WHERE id = #id# ⑺ ]]> …… ⑴ ID 指定了操作ID,之后我们可以在代码中通过指定操作id 来执行此节点所定义的操作,如:sqlMap.update("updateUser",user); ID 设定使得在一个配置文件中定义两个同名节点成为可能(两个update 节点,以不同id 区分) ⑵ parameterClass 指定了操作所需的参数类型,此例中update 操作以com.ibatis.sample.User 类型的对象作为参数,目标是将提供的User 实例更新到数据库parameterClass="user"中,user 为“com.ibatis.sample.User” 类的别名,别名可通过typeAlias 节点指定,如示例配置文件中的:⑶ 通过节点,可以避免SQL 中与XML 规范相冲突的字符对XML 映射文件的合法性造成影响。⑷ 执行更新操作的SQL,这里的SQL 即实际数据库支持的SQL 语句, 将由ibatis 填入参数后交给数据库执行。⑸ SQL 中所需的用户名参数,“#name#”在运行期会由传入的user 对象的name 属性填充。⑹ SQL 中所需的用户性别参数“#sex#”, 将在运行期由传入的user 对象的sex 属性填充。⑺ SQL 中所需的条件参数“#id#”, 将在运行期由传入的user 对象的id 属性填充。对于这个示例,ibatis 在运行期会读取id 为“updateUser”的update 节点的SQL 定义,并调用指定的user 对象的对应getter 方法获取属性值,并用此属性值,对SQL 中的参数进行填充后提交数据库执行。此例对应的应用级代码如下,其中演示了的基本使用方法:String resource ="com/ibatis/sample/SqlMapConfig.xml"; Reader reader; ibatis SQLMap reader = Resources.getResourceAsReader(resource); XmlSqlMapClientBuilder xmlBuilder =new XmlSqlMapClientBuilder(); SqlMapClient sqlMap = xmlBuilder.buildSqlMap(reader); //sqlMap系统初始化完毕,开始执行update操作try{ sqlMap.startTransaction(); User user = new User(); user.setId(new Integer(1)); user.setName("Erica"); user.setSex(new Integer(1)); sqlMap.update("updateUser",user); sqlMap.commitTransaction(); finally{ sqlMap.endTransaction(); } 其中,SqlMapClient 是ibatis 运作的核心,所有操作均通过SqlMapClient 实例完成。可以看出,对于应用层而言,程序员面对的是传统意义上的数据对象,而非JDBC 中烦杂的ResultSet,这使得上层逻辑开发人员的工作量大大减轻,同时代码更加清晰简洁。数据库操作在映射文件中加以定义,从而将数据存储逻辑从上层逻辑代码中独立出来。而底层数据操作的SQL 可配置化,使得我们可以控制最终的数据操作方式,通过SQL 的优化获得最佳的数据库执行效能,这在依赖SQL 自动生成的“全自动”ORM 机制中是所难以实现的。ibatis 配置结合上面示例中的ibatis 配置文件。下面是对配置文件中各节点的说明: PUBLIC "> <settings ⑴cacheModelsEnabled=enhancementEnabled=lazyLoadingEnabled=errorTracingEnabled=maxRequests=maxSessions=maxTransactions=useStatementNamespaces=/> ⑵ ⑶<property name="JDBC.Driver" value="com.p6spy.engine.spy.P6SpyDriver"/> <property name="JDBC.ConnectionURL" value="jdbc:mysql://localhost/sample"/> <property name=value="10"/> <property name=value="120000"/> <property name="Pool.PingQuery" ="select 1 from ACCOUNT"/> <property name=value="1"/> <property name=value="1"/> <!DOCTYPE sqlMapConfig "-//iBATIS.com//DTD SQL Map Config 2.0//EN" "http://www.ibatis.com/dtd/sql-map-config-2.dtd"true" "true" "true" "true" "32" "10" "5" "false" valuevalue"Pool.MaximumActiveConnections" "Pool.MaximumIdleConnections" "Pool.MaximumCheckoutTime" valuevaluevalue"Pool.PingConnectionsOlderThan" "Pool.PingConnectionsNotUsedFor" ⑷ ⑴ Settings 节点参数描述cacheModelsEnabled 是否启用SqlMapClient 上的缓存机制。建议设为"true" enhancementEnabled 是否针对POJO 启用字节码增强机制以提升getter/setter 的调用效能,避免使用Java Reflect 所带来的性能开销。同时,这也为Lazy Loading 带来了极大的性能提升。建议设为"true" errorTracingEnabled 是否启用错误日志,在开发期间建议设为"true" 以方便调试lazyLoadingEnabled 是否启用延迟加载机制,建议设为"true" maxRequests 最大并发请求数(Statement 并发数) maxTransactions 最大并发事务数maxSessions 最大Session 数。即当前最大允许的并发SqlMapClient 数。maxSessions 设定必须介于maxTransactions 和maxRequests 之间,即maxTransactions<maxSessions=< maxRequests useStatementNamespaces 是否使用Statement 命名空间。这里的命名空间指的是映射文件中,sqlMap 节点的namespace 属性,如在上例中针对t_user 表的映射文件sqlMap 节点: 这里,指定了此sqlMap 节点下定义的操作均从属于"User"命名空间。在useStatementNamespaces="true"的情况下,Statement 调用需追加命名空间,如:⑵ transactionManager 节点sqlMap.update("User.updateUser",use r); 否则直接通过Statement 名称调用即sqlMap.update("updateUser",user); 但请注意此时需要保证所有映射定义无重名。可,如:文件中,Statement transactionManager 节点定义了ibatis 的事务管理器,目前提供了以下几种选择: . JDBC 通过传统JDBC Connection.commit/rollback 实现事务支持。. JTA 使用容器提供的JTA 服务实现全局事务管理。. EXTERNAL 外部事务管理, 如在EJB 中使用ibatis,通过EJB 的部署配置即可实现自动的事务管理机制。此时ibatis 将把所有事务委托给外部容器进行管理。此外,通过Spring 等轻量级容器实现事务的配置化管理也是一个不错的选择。关于结合容器实现事务管理,参见“高级特性”中的描述。⑶ dataSource 节点dataSource 从属于transactionManager 节点,用于设定ibatis 运行期使用的DataSource 属性。type 属性: dataSource 节点的type属性指定了dataSource 的实现类型。可选项目: . SIMPLE: SIMPLE 是ibatis 内置的dataSource 实现,其中实现了一个简单的数据库连接池机制,对应ibatis 实现类为com.ibatis.sqlmap.engine.datasource.SimpleDataSourceFactory 。. DBCP: 基于Apache DBCP 连接池组件实现的DataSource 封装,当无容器提供DataSource 服务时,建议使用该选项,对应ibatis 实现类为com.ibatis.sqlmap.engine.datasource.DbcpDataSourceFactory 。. JNDI: 使用J2EE 容器提供的DataSource 实现,DataSource 将通过指定的JNDI Name 从容器中获取。对应ibatis 实现类为com.ibatis.sqlmap.engine.datasource.JndiDataSourceFacto ry。dataSource 的子节点说明(SIMPLE

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值