Java JDBC 2

Connection

与特定数据库的一个连接(会话),执行SQL语句并在连接的上下文中返回结果。

  • 数据库信息
// 数据库元信息包括:数据库表、SQL语法、存储过程、连接的功能等。
DatabaseMetaData getMetaData();
  • 连接属性
// 自动提交:默认是
// 自动提交时,所有SQL都以单个事务执行和提交;
//     语句完成时进行提交,完成的时机取决于语句类型:
//     DML语句:完成执行时提交事务
//     Select语句:ResultSet关闭时提交
//     CallableStatement:所有ResultSet关闭、所有更新数和输出参数检索完
// 非自动提交时,多个SQL组成一个事务,显式提交或回滚事务。
boolean getAutoCommit();
void setAutoCommit(boolean autoCommit);

// 只读:
boolean isReadOnly();
// 此方法不能在事务中调用
void setReadOnly(boolean readOnly);

// ResultSet保持性
// ResultSet.HOLD_CURSORS_OVER_COMMIT 事务提交时ResultSet仍打开
// ResultSet.CLOSE_CURSORS_AT_COMMIT 事务提交时ResultSet关闭
int getHoldability();
void setHoldability(int holdability);

// 事务级别:
// Connection.TRANSACTION_NONE:0,无事务
// Connection.TRANSACTION_READ_UNCOMMITTED:1,未提交读
// Connection.TRANSACTION_READ_COMMITTED:2,已提交读
// Connection.TRANSACTION_REPEATABLE_READ:4,可重复读
// Connection.TRANSACTION_SERIALIZABLE:8,串行化
int getTransactionIsolation();
void setTransactionIsolation(int level);

  • 创建Statement
// resultSetType:
//     ResultSet.TYPE_FORWARD_ONLY
// resultSetConcurrency:
//     ResultSet.CONCUR_READ_ONLY
// holdability:
//     getHoldability()
Statement createStatement();

Statement createStatement(int resultSetType, int resultSetConcurrency);

// resultSetType: 
//     ResultSet.TYPE_FORWARD_ONLY
//     ResultSet.TYPE_SCROLL_INSENSITIVE
//     ResultSet.TYPE_SCROLL_SENSITIVE
// resultSetConcurrency:
//     ResultSet.CONCUR_READ_ONLY
//     ResultSet.CONCUR_UPDATABLE
// resultSetHoldability:
//     ResultSet.HOLD_CURSORS_OVER_COMMIT
//     ResultSet.CLOSE_CURSORS_AT_COMMIT
Statement createStatement(int resultSetType, int resultSetConcurrency, 
        int resultSetHoldability)
  • 创建PreparedStatement
PreparedStatement prepareStatement(String sql);
PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency);
PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, 
        int resultSetHoldability);

PreparedStatement prepareStatement(String sql, int autoGeneratedKeys);
PreparedStatement prepareStatement(String sql, int columnIndexes[]);
PreparedStatement prepareStatement(String sql, String columnNames[]);
  • 创建CallableStatement
CallableStatement prepareCall(String sql);
CallableStatement prepareCall(String sql, int resultSetType,int resultSetConcurrency);
CallableStatement prepareCall(String sql, int resultSetType,int resultSetConcurrency,
        int resultSetHoldability);
  • 事务
// 非自动提交时,使用此方法显式提交事务。
// 提交上次commit/rollback之后的所有更改,释放Connection持有的所有数据库锁
void commit();

// 非自动提交是,使用此方法回滚事务。
// 撤销上次commit/rollback之后的所有更改,释放Connection持有的所有数据库锁
void rollback();
  • 保存点
// 在当前事务中创建一个命名保存点
// 如果在事务外调用,则启动一个新事务
Savepoint setSavepoint(String name);
// 创建一个匿名保存点
Savepoint setSavepoint();

// 回滚到指定保存点(只在非自动提交时调用)
void rollback(Savepoint savepoint);

void releaseSavepoint(Savepoint savepoint);
  • 关闭连接
// 立即释放Connection持有的数据库和JDBC资源,而不是等待自动释放。
// 如果已关闭,调用此方法则无操作。
// 强烈建议在调用此方法前,显式调用commit或rollback方法。
void close();

// 查询连接是否关闭。
// 连接在调用close或发生特定错误时被关闭
// 此方法并不用来判断连接是否有效。典型的是通过捕获操作异常来判断连接失效。
boolean isClosed();

// 驱动发送查询语句或使用其他方式已检查连接是否有效
// 有效返回true,无效返回false,超时抛出异常
boolean isValid(int timeout);
  • 类型映射
Map<String,Class<?>> getTypeMap();
void setTypeMap(Map<String,Class<?>> map);

DatabaseMetaData

// 获取数据库
// 返回:ResultSet列
//     TABLE_CAT:String,数据库名
ResultSet getCatalogs();

// 获取表
// catalog:数据库,null表示不指定
// schemaPattern:表模式
// tableNamePattern:表名模式
// types:表类型,见TABLE_TYPE
// 返回:ResultSet列
//     TABLE_CAT:String,数据库名,可能为null
//     TABLE_SCHEM:String,表模式,可能为null
//     TABLE_NAME:String,表名
//     TABLE_TYPE:String,表类型,有TABLE,VIEW,SYSTEM TABLE,
//          GLOBAL TEMPORARY,LOCAL TEMPORARY,ALIAS,SYNONYM
//     REMARKS:String,表备注
ResultSet getTables(String catalog, String schemaPattern, 
        String tableNamePattern, String types[]);

// 获取列
// catalog:数据库,null表示不指定
// schemaPattern:表模式
// tableNamePattern:表名模式
// columnNamePattern:列名模式
// 返回:ResultSet列
//     TABLE_CAT:String,数据库名,可能为null
//     TABLE_SCHEM:String,表模式,可能为null
//     TABLE_NAME:String,表名
//     COLUMN_NAME:String,列名
//     DATA_TYPE:int,类型,见java.sql.Types
//     TYPE_NAME:String,类型名
//     COLUMN_SIZE:int,列大小
//     REMARKS:String,备注
//     COLUMN_DEF:String,默认值,可能为null
//     ORDINAL_POSITION:int,列顺序,1起
//     IS_NULLABLE:String:YES,可以保护NULL;NO,不能;空白,未知;
//     IS_AUTOINCREMENT:String:YES,自增;NO,不是;空白,未知;
ResultSet getColumns(String catalog, String schemaPattern, 
        String tableNamePattern, String columnNamePattern);

// 获取索引
// catalog:数据库,null表示不指定
// schema:表模式
// table:表名,不允许为null
// unique:唯一索引
// approximate:
// 返回:ResultSet列
//     TABLE_CAT:String,数据库名,可能为null
//     TABLE_SCHEM:String,表模式,可能为null
//     TABLE_NAME:String,表名
//     INDEX_NAME:String,索引名
//     NON_UNIQUE:boolean,是否不是唯一索引
//     COLUMN_NAME:String,索引列名
//     ORDINAL_POSITION:short,索引列名位置
ResultSet getIndexInfo(String catalog, String schema, String table,
        boolean unique, boolean approximate);

获取数据库表结构

  • 表结构Bean
public class Database {
    /** 数据库名 */
    private String databaseName;

    /** 表 */
    private Map<String, Table> tables = new LinkedHashMap<>();

    static class Table {
        /** 表名 */
        String tableName;
        /** 备注 */
        String comment;
        /** 列 */
        Map<String, Column> columns = new LinkedHashMap<>();
        /** 索引 */
        Map<String, Index> indexes = new LinkedHashMap<>();
    }

    static class Column {
        /** 列名 */
        String columnName;
        /** 类型,见java.sql.Types */
        int dataType;
        /** 类型名 */
        String typeName;
        /** 大小*/
        int columnSize;
        /** null,未知;true,yes;false,no;*/
        Boolean nullable;
        /** 默认值 */
        String defaultValue;
        /** null,未知;true,yes;false,no;*/
        Boolean autoIncrement;
        /** 备注 */
        String comment;
    }

    static class Index {
        /** 索引名 */
        private String indexName;
        /** 是否是唯一索引 */
        private boolean unique;
        /** 列名 */
        private List<String> columnNames = new ArrayList<>();
    }

    // 打印表结构
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append(databaseName + ": " + tables.size() + "\n\n");

        for (Table table : tables.values()) {
            sb.append("CREATE TABLE `" + table.tableName + "` (\n");

            for (Column column : table.columns.values()) {
                sb.append("    `" + column.columnName + "` " + column.typeName + "(" + column.columnSize + ")");
                if (column.nullable != null && !column.nullable) {
                    sb.append(" NOT NULL");
                }
                if (StringUtils.isNotBlank(column.defaultValue)) {
                    sb.append(" DEFAULT '" + column.defaultValue + "'");
                }
                if (column.autoIncrement != null && column.autoIncrement) {
                    sb.append(" AUTO_INCREMENT");
                }
                if (StringUtils.isNotBlank(column.comment)) {
                    sb.append(" COMMENT '" + column.comment + "'");
                }
                sb.append(",\n");
            }

            for (Index index : table.indexes.values()) {
                sb.append("    ");
                String indexName = index.indexName;
                if (StringUtils.equalsIgnoreCase(indexName, "PRIMARY")) {
                    sb.append("PRIMARY KEY");
                } else if (index.unique) {
                    sb.append("UNIQUE KEY `" + indexName + "`");
                } else {
                    sb.append("KEY `" + indexName + "`");
                }
                sb.append(" (");
                for (int j = 0; j < index.columnNames.size(); j++) {
                    String columnName = index.columnNames.get(j);
                    sb.append("`" + columnName + "`");
                    if (j != index.columnNames.size()-1) {
                        sb.append(", ");
                    }
                }
                sb.append("),\n");
            }

            // 去掉最后一行的“,”
            sb.replace(sb.length()-2, sb.length(), "\n)");

            if (StringUtils.isNotBlank(table.comment)) {
                sb.append(" COMMENT='" + table.comment + "'");
            }

            sb.append(";\n\n");
        }
        return sb.toString();
    }
}
  • 获取表结构
public static Map<String, Database> getDatabases(String url, String database, String table) {
    Map<String, Database> databaseMap = new HashMap<>();

    Connection connection = null;
    ResultSet resultSet = null;
    try {
        Properties info = new Properties();
        connection = DriverManager.getConnection(url, info);

        DatabaseMetaData metaData = connection.getMetaData();

        // Database
        {
            List<String> databaseNames = new ArrayList<>();

            if (database != null) {
                databaseNames.add(database);
            } else {
                Set<String> systemDatabases = new HashSet<>();
                // MySQL
                systemDatabases.add("information_schema");
                systemDatabases.add("performance_schema");

                resultSet = metaData.getCatalogs();
                while (resultSet.next()) {
                    String databaseName = resultSet.getString("TABLE_CAT");
                    if (!systemDatabases.contains(databaseName)) {
                        databaseNames.add(databaseName);
                    }
                }
                resultSet.close();
            }

            for (String databaseName : databaseNames) {
                databaseMap.put(database, new Database(databaseName));
            }
        }

        // Table
        {
            resultSet = metaData.getTables(null, null, table, new String[]{"TABLE"});
            while (resultSet.next()) {
                String databaseName = resultSet.getString("TABLE_CAT");

                String tableName = resultSet.getString("TABLE_NAME");

                // Type: TABLE, VIEW, SYSTEM TABLE, GLOBAL TEMPORARY, LOCAL TEMPORARY, ALIAS, SYNONYM
                // String tableType = resultSet.getString("TABLE_TYPE");
                
                String comment = resultSet.getString("REMARKS");

                databaseMap.get(databaseName).addTable(tableName, comment);
            }
            resultSet.close();
        }

        // Column
        {
            resultSet = metaData.getColumns(null, null, table, null);
            while (resultSet.next()) {
                String databaseName = resultSet.getString("TABLE_CAT");

                String tableName = resultSet.getString("TABLE_NAME");

                String columnName = resultSet.getString("COLUMN_NAME");

                // SQL type from java.sql.Types
                int dataType = resultSet.getInt("DATA_TYPE");

                // Data source dependent type name, for a UDT the type name is fully qualified
                String typeName = resultSet.getString("TYPE_NAME");

                int columnSize = resultSet.getInt("COLUMN_SIZE");

                String comment = resultSet.getString("REMARKS");

                // may be null
                String defaultValue = resultSet.getString("COLUMN_DEF");

                // YES: include NULLs
                // NO: not include NULLs
                // empty string: nullability is unknown
                String isNullable = resultSet.getString("IS_NULLABLE");
                Boolean nullable = null;
                if (StringUtils.equalsIgnoreCase(isNullable, "YES")) {
                    nullable = true;
                } else if (StringUtils.equalsIgnoreCase(isNullable, "NO")) {
                    nullable = false;
                }

                // YES: is auto incremented
                // NO: is not auto incremented
                // empty string: unknown
                String IS_AUTOINCREMENT = resultSet.getString("IS_AUTOINCREMENT");
                Boolean autoIncrement = null;
                if (StringUtils.equalsIgnoreCase(IS_AUTOINCREMENT, "YES")) {
                    autoIncrement = true;
                } else if (StringUtils.equalsIgnoreCase(IS_AUTOINCREMENT, "NO")) {
                    autoIncrement = false;
                }

                databaseMap.get(databaseName).addColumn(tableName, columnName, dataType, typeName, columnSize, nullable, defaultValue, autoIncrement, comment);
            }
            resultSet.close();
        }

        // Index
        {
            for (Database db : databaseMap.values()) {
                for (String tb : db.getTableNames()) {
                    resultSet = metaData.getIndexInfo(db.getDatabaseName(), null, tb, false, false);
                    while (resultSet.next()) {
                        String databaseName = resultSet.getString("TABLE_CAT");

                        String tableName = resultSet.getString("TABLE_NAME");

                        String indexName = resultSet.getString("INDEX_NAME");

                        boolean unique = !resultSet.getBoolean("NON_UNIQUE");

                        String columnName = resultSet.getString("COLUMN_NAME");

                        databaseMap.get(databaseName).addIndex(tableName, indexName, unique, columnName);
                    }
                    resultSet.close();
                }
            }
        }

    } catch (SQLException e) {
        e.printStackTrace();
    } finally {
        if (connection != null) {
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    return databaseMap;
}
  • 测试
public static void main(String[] args) {
    String url = "jdbc:mysql://localhost:3306/test?user=root&password=123456&useUnicode=true&characterEncoding=UTF-8";
    Map<String, Database> databaseMap = SchemaUtil.getDatabases(url, "test", "user");
    for (Database database : databaseMap.values()) {
       System.out.println(database);
   }
}

结果:

test: 1

CREATE TABLE `user` (
    `id` BIGINT(19) NOT NULL AUTO_INCREMENT,
    `userName` VARCHAR(128) NOT NULL COMMENT '账号',
    `nickName` VARCHAR(100) NOT NULL,
    `status` TINYINT(3) DEFAULT '0',
    `createTime` BIGINT(19) NOT NULL,
    `updateTime` BIGINT(19) NOT NULL,
    PRIMARY KEY (`id`),
    UNIQUE KEY `u_s` (`userName`, `status`),
    KEY `key_un` (`userName`)
);
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,463评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,868评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,213评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,666评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,759评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,725评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,716评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,484评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,928评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,233评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,393评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,073评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,718评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,308评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,538评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,338评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,260评论 2 352

推荐阅读更多精彩内容