说明
本篇文章重点介绍了使用JDBC API对数据库进行增删改查、执行存储过程中使用到的类和常用方法,不常用的并没有介绍,只能算作是对JDBC这部分知识的一个入门。还介绍了第三方数据库连接池DBCP和C3P0的代码实现。Java JDBC对数据库进行操作可分为四个步骤:
- 加载并注册驱动
- 获取Connection
- 操作数据库
- 关闭连接释放JDBC和数据库资源
这里主要介绍步骤2、3涉及到类和相关方法。在正式开始介绍之前我们需要了解一下SQL语言的分类。
SQL语言的分类
在使用JDBC API执行DQL总是返回一个结果集对象(ResultSet),执行DML总是返回受影响的行数,其它的语言大多数情况下返回0。
Connection的获取
获取Connection可以从实现了DataSource接口的数据库连接池获取,也可以通过DriverManager获取。我们先介绍一下java.slq.Conection接口,然后介绍通过连接池和DriverManager获取数据库连接。
java.sql.Connection接口
作用:代表与特定数据库的连接(会话),在连接上执行SQL并返回结果。
常用方法如下:
Statement createStatement() throws SQLException
创建一个Statement对象来将不带参数占位符的SQL发送到数据库执行。
PreparedStatement prepareStatement(String sql) throws SQLException
创建一个PreparedStatement对象将带参数占位符的SQL发送到数据库执行。PreperedStatement会预编译SQL,如果一个SQL需要重复执行多次使用PreparedStatement会提高执行效率。
CallableStatement prepareCall(String sql) throws SQLException
创建一个CallableStatement对象用于调用数据库的存储过程。
通过java.sql.DriverManager类获取Connection
作用:用于管理一组JDBC驱动程序。我们来看一下DriverManager的经典应用:
//加载mysql驱动类,并调用DriverManager.registerDriver()向DriverManager注册驱动实例
mysql url 事例:"jdbc:mysql://localhost:3306/jiemian_db?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true";
oracle url 事例:jdbc:oracle:thin:@192.168.9.87:1521:jrdb
代码:
//加载mysql驱动类,并调用DriverManager.registerDriver()向DriverManager注册驱动实例
Class.forName(DBInfo.DIRVERCLASSNAME);
Connection conn = DriverManager.getConnection(DBInfo.URL, DBInfo.USERNAME, DBInfo.PASSWORD);
我们可以在通过调用Class.forName("oracle的JDBC驱动")代码,向DriverManager注册oracle驱动实例,这样我们向DriverManger注册了mysql和oracle驱动实例,我们通过调用DriverManager.getConnection()会根据传入的url找到合适的驱动。JDBC规定URL的格式为jdbc:subprotocol:subname。
mysql url 事例:"jdbc:mysql://localhost:3306/jiemian_db?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true";
oracle url 事例:jdbc:oracle:thin:@192.168.9.87:1521:jrdb
代码:
/**
* 通过java.sql.DriverManager获取Connection
* @author Administrator
*
*/
public class ConnectionByDriverManager {
public static Connection getConnection() throws Exception{
Connection conn = null;
try {
//加载mysql驱动类,之后创建mysql创建实例,并向ManagerDrivers注册该实例
Class.forName(DBInfo.DIRVERCLASSNAME);
conn = DriverManager.getConnection(DBInfo.URL, DBInfo.USERNAME, DBInfo.PASSWORD);
System.out.println(conn!=null);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
throw e;
}
return conn;
}
public static void main(String [] args){
try {
Connection conn = ConnectionByDriverManager.getConnection();
conn.close();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
通过C3P0连接池获取Connection
spring框架使用的就是C3P0实现的数据库连接池。
package com.zhangxy.jdbcAPITest;
import java.sql.Connection;
import java.sql.SQLException;
import com.mchange.v2.c3p0.ComboPooledDataSource;
/**
* 第三方数据库驱动C3P0实现的数据库连接池
* Spring的数据库连接池是使用C3P0实现的
* @author Administrator
*
*/
public class C3P0Pool {
private static ComboPooledDataSource cpds=null;
static {
//C3P0读取指定目录下的c3p0-config.xml文件来实现连接池的配置
//这里是编译后class所有在路径
String path = C3P0Pool.class.getResource("").getFile()+"c3p0-config.xml";
//System.out.println("path:"+path);
//设置c3p0-config.xml文件读取的路径,如果不设置默认读取src目录下的c3p0-config.xml文件
System.setProperty("com.mchange.v2.c3p0.cfg.xml",path);
//ComboPooledDataSource(String configName),configName代表c3p0-config.xml文件中指定的named-config节点下的连接池初始化参数
//如果不指定,将读取defalut-config节点下的连接池初始化参数
cpds = new ComboPooledDataSource();
}
public static Connection getConnection() throws SQLException{
return cpds.getConnection();
}
public static void main(String [] args){
SQLException e1 = null;
try {
Connection conn = C3P0Pool.getConnection();
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e1=e;
e.printStackTrace();
}
System.out.println("连接是否成功:"+(e1==null));
}
}
c3p0-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<default-config>
<property name="jdbcUrl">
jdbc:mysql://localhost:3306/jiemian_db
</property>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="user">root</property>
<property name="password">123456</property>
<property name="checkoutTimeout">3000</property>
<property name="idleConnectionTestPeriod">30</property>
<property name="initialPoolSize">10</property>
<property name="maxIdleTime">30</property>
<property name="maxPoolSize">100</property>
<property name="minPoolSize">10</property>
<property name="maxStatements">200</property>
</default-config>
<named-config name="test_db">
<property name="jdbcUrl">
jdbc:oracle:thin:@10.110.26.26:1521:crmdb4
</property>
<property name="driverClass">oracle.jdbc.driver.OracleDriver</property>
<property name="user">dbmarketadm</property>
<property name="password">qwer5678</property>
<property name="checkoutTimeout">3000</property>
<property name="idleConnectionTestPeriod">30</property>
<property name="initialPoolSize">10</property>
<property name="maxIdleTime">30</property>
<property name="maxPoolSize">100</property>
<property name="minPoolSize">10</property>
<property name="maxStatements">200</property>
</named-config>
</c3p0-config>
c3p0读取c3p0-config参考:
http://blog.csdn.net/soyuone/article/details/51554263
通过DBCP连接池获取Connection
package com.zhangxy.jdbcAPITest;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.apache.commons.dbcp.BasicDataSource;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
/**
* 通过DBCP创建连接池
* DBCP并没有带有像C3P0一样可以从XML文件读取初始化连接池的配置,但是这里通过反射自己实现了这个功能
* @author Administrator
*
*/
public class DBCPPool {
private static final String DBCP_CONFIG_PATH = DBCPPool.class.getResource("").getFile()+"dbcp-config.xml";
private static BasicDataSource baseDs=null;
static {
init();
}
public static void init(){
try {
Class<?> clazz = Class.forName("org.apache.commons.dbcp.BasicDataSource");
Object inst = clazz.newInstance();
List<FieldInfo> list = getConfig();
for(Iterator<FieldInfo> it = list.iterator();it.hasNext();){
FieldInfo fieldInfo = it.next();
String fName = fieldInfo.getName();
String fValue = fieldInfo.getValue();
String methodName = "set"+fName.substring(0,1).toUpperCase()+fName.substring(1, fName.length());
Field properties = clazz.getDeclaredField(fName);
Method method = clazz.getDeclaredMethod(methodName, properties.getType());
method.invoke(inst, FieldInfo.getTrueTypeValue(fValue,properties.getType()));
}
baseDs = (BasicDataSource) inst;
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchFieldException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private static List<FieldInfo> getConfig(){
List<FieldInfo> list = new ArrayList<FieldInfo>();
File file = new File(DBCP_CONFIG_PATH);
SAXReader reader = new SAXReader();
try {
Document doc = reader.read(file);
Element rootEl = doc.getRootElement();
for(Iterator<Element> it = rootEl.element("default-config").elementIterator("property");it.hasNext();){
Element propertyEl = it.next();
FieldInfo fieldInfo = new FieldInfo();
fieldInfo.setName(propertyEl.attributeValue("name"));
fieldInfo.setType(propertyEl.attributeValue("type"));
fieldInfo.setValue(propertyEl.getTextTrim());
//System.out.println(fieldInfo.toString());
list.add(fieldInfo);
}
} catch (DocumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return list;
}
public static Connection getConnection() throws SQLException{
return baseDs.getConnection();
}
public static void main(String [] args){
//Object obj = 12;
//System.out.print((obj instanceof String));
try {
Connection conn = DBCPPool.getConnection();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
class FieldInfo{
private String name;
private String value;
private String type;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
@Override
public String toString() {
return "name:"+getName()+" type:"+getType()+" value:"+getValue();
}
public static Object getTrueTypeValue(String value, Class<?> type){
if(type == int.class||type==Integer.class){
return Integer.valueOf(value);
}else if(type==long.class||type==Long.class){
return Long.valueOf(value);
}else if(type==boolean.class||type==Boolean.class){
return Boolean.valueOf(value);
}
return value;
}
}
dbcp-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<dbcp-config>
<default-config>
<property type="string" name="url">
jdbc:mysql://localhost:3306/jiemian_db
</property>
<property name="driverClassName">com.mysql.jdbc.Driver</property>
<property name="username">root</property>
<property name="password">123456</property>
<property name="initialSize">30</property>
<property name="maxActive">20</property>
<property name="maxIdle">10</property>
<property name="minIdle">5</property>
<property name="maxWait">3000</property>
</default-config>
</dbcp-config>
第三方连接池都实现了javax.sql.DataSource接口
作用:用于获取DataSource代表的物理数据库的连接(Conection对象)。该工厂用于提供到此 DataSource 对象所表示的物理数据源的连接。作为 DriverManager 工具的替代项,DataSource 对象是获取连接的首选方法。实现 DataSource 接口的对象通常在基于 JavaTM Naming and Directory Interface (JNDI) API 的命名服务中注册。
DataSource 接口由驱动程序供应商实现。共有三种类型的实现:
- 基本实现 - 生成标准的 Connection 对象
- 连接池实现 - 生成自动参与连接池的 Connection 对象。此实现与中间层连接池管理器一起使用。
- 分布式事务实现 - 生成一个 Connection 对象,该对象可用于分布式事务,大多数情况下总是参与连接池。此实现与中间层事务管理器一起使用,大多数情况下总是与连接池管理器一起使用。
通过 DataSource 对象访问的驱动程序本身不会向 DriverManager 注册。通过查找操作获取 DataSource 对象,然后使用该对象创建 Connection 对象。使用基本的实现,通过 DataSource 对象获取的连接与通过 DriverManager 设施获取的连接相同。
Connection getConnection() throws SQLException
尝试建立与此 DataSource 对象所表示的数据源的连接。
Connection getConnection(String username, String password)throws SQLException
尝试建立与此 DataSource 对象所表示的数据源的连接。
对数据库操作(增删改查)
对数据库进行操作有这么几个常用的接口和类:java.sql.Statement接口、java.sql.PreparedStatement接口(派生自Statemment接口)、java.sql.ResultSet接口,下面分别介绍。
java.sql.Statement接口
作用:用于执行静态SQL语句并返回生成结果集对象或者是受影响的行数。
方法:
方法:
1,int executeUpdate(String sql) throws SQLException
用于执行DML和DDL语言,对于DML语句返回受影响的行数,DDL语句返回0.
2,ResultSet executeQuery(String sql) throws SQLException
用于执行DQL语句,返回一个结果集对象。即使没有查询到结果也不会返回一个null,而是返回一个empty的ResultSet对象。
3,boolean execute() throws SQLException
执行指定的SQL语句,该语句可以是任何类型。返回true表示执行SQL是一个结果集,可以通过getResultSet()获取。false表示执行SQL是一个受影响的计数(通常执行的是DML语句)或无返回结果(通常执行的是DDL语句),可以通过getUpdateCount()获取。
4,boolean getMoreResults() throws SQLException
移动到当前Statement对象的下一个结果集。返回true表示表示当前位置是一个ResultSet对象,可以通过getResultSet()获取 。返回false表示当期是一个计数或没有更多结果,可以通过getUpdateCount()获取。如下表达式为true表示么有更多结果集:(statement.getMoreResultSets()==false && statement.getUpdateCount()==-1)
5,ResultSet getResultSet() throws SQLException
以ResultSet对象方式获取当前结果。
6,int getUpdateCount() throws SQLException
以受影响的行数方式获取当前结果。如果当前结果集为ResultSet形式或没有更多结果返回-1.
java.sql.PreparedStatement接口,派生自Statemment接口
作用:表示执行预编译的SQL语句返回生成结果集对象或者是受y影响的行数。
方法:
方法:
1,void setString(int parameterIndex,String x) throws SQLException
PreparedStatement接口有很多setXXXXX(index,x)方法,作用是给参数占位符设置java中类型的值。这里已setString()方法作为说明,index为参数占位符在SQL语句中的位置,从1开始,x代表要设置的值。
java.sql.ResultSet接口
作用:表示结果集,通常由执行select语句生产。 当我们查询得到一个ResultSet对象,这个对象不是在客户端的内存中存储所有查询到的数据,而是和数据库查询的表有个连接,调用next()遍历ResultSet是从数据库实时获取需要的列值。
1,boolean first() throws SQLException
将光标移动到ResutSet对象的第一行。如果光标有效返回true,否则返回false
2,boolean next() throws SQLException
将光标移动到下一行,如果存在下一行返回true,否则返回false。while(resultSet.next())可遍历ResultSet对象。一开始光标是在结果集第一行之前的,首次调用next()会将光标移动到第一行。当next()返回false光标在最后一行之后。
对数据库进行增删改查操作综合事例
package com.zhangxy.jdbcAPITest;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class OPStudent {
public static void closeAll(Connection conn, Statement state, ResultSet rs){
try {
if(conn!=null&&!conn.isClosed()){
conn.close();
}
if(state!=null&&!state.isClosed()){
state.close();
}
if(rs!=null&&!rs.isClosed()){
rs.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 创建Student表
*/
public static void createStudentTable(Connection conn){
Statement state = null;
try {
String dropTabSql = "drop table if exists student"
,createTabSql = "create table student(NO char(20),name varchar(20),primary key(NO))";
state = conn.createStatement();
//执行两条DDL语句,一下两行码执行时,没有抛出SQLException则证明执行成功
state.executeUpdate(dropTabSql);
state.executeUpdate(createTabSql);
System.out.println("createStudentTable:成功。");
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
closeAll(conn,state,null);
}
}
/**
* 向Student表插入数据
* @param conn
*/
public static void insertStudent(Connection conn){
PreparedStatement pState = null;
int c = 0;
try {
pState = conn.prepareStatement("insert into student(no,name) values(?,?)");
pState.setString(1, "120000");
pState.setString(2, "张三");
c = pState.executeUpdate();
System.out.println("insert into 受影响行数:"+c);
pState.clearParameters();
pState.setString(1, "130000");
pState.setString(2, "李四");
c = pState.executeUpdate();
System.out.println("insert into 受影响行数:"+c);
//如执行SQL条数很多建议使用批处理
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
closeAll(conn,pState,null);
}
}
/**
* 查询Student表数据
* @param conn
*/
public static void queryStudent(Connection conn){
Statement state = null;
try {
boolean isEmptyRs = true;
state = conn.createStatement();
ResultSet rs = state.executeQuery("select * from student"); while (rs.next()) {
isEmptyRs = false;
String no = rs.getString("no");
String name = rs.getString("name");
System.out.println("no:"+no+" name:"+name);
}
//如果只想判断ResultSet是否是empty,不想遍历,可以使用一下代码
//isEmptyRs=!rs.next();//光标移动到结果集的第一行,成功返回true,否则false
//rs.previous();//光标移动到结果集第一行之前的位置
System.out.println("查询没有结果:"+isEmptyRs);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
closeAll(conn,state,null);
}
}
/**
* 根据no更新Student表(使用参数占位符)
* @param conn
* @param no
* @return
*/
public static int updStudentByNo(Connection conn, String no, String name){
int count=0;
String sql = "update student t set t.name=? where t.no=?";
try {
PreparedStatement pState = conn.prepareStatement(sql);
pState.setString(1, name);
pState.setString(2, no);
count = pState.executeUpdate();
System.out.println(String.format("update student by no:[%s] name:[%s] 受营销的函数[%d]",no,name,count));
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return count;
}
public static void main(String [] args){
try {
createStudentTable( DBCPPool.getConnection() );
insertStudent( DBCPPool.getConnection() );
queryStudent( DBCPPool.getConnection() );
updStudentByNo( DBCPPool.getConnection(), "120000", "abcefg");
queryStudent( DBCPPool.getConnection() );
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
JDBC API执行数据库的存储过程
JDBC API执行数据库存储过程主要涉及到java.sql.CallableStatement接口。
java.sql.CallableStatement接口
作用:用于调用存储过程。
调用存储过程的参数可以使用参数占位符.给存储过程的in参数传入值时,使用CallableStatement对象的set方法(例如pState.setString(1,"abc"))。给存储过程的out参数传入值时,使用CallabelStatement对象的registerOutParameter()方法向CallableStatement对象注册out参数,执行完存储过程之后,调用get方法获取返回的out参数。
JDBC API对调用的存储过程进行了转移,转义后对所有关系行数据库都能使用统一的方式调用存储过程,兼容所有数据库。义语法有一个包含结果参数的形式和一个不包含结果参数的形式。
包含结果参数的:{?= call <procedure-name>[(<arg1>,<arg2>, ...)]},我在开发中使用的Mysql数据库,Mysql数据库的存储过程应该不能返回值(return 值),返回的值都是通过out参数带出,或者是使用select语句返回结果集。不知道其它数据库能不能返回一个值。
不包含结果参数的:{call <procedure-name>[(<arg1>,<arg2>, ...)]}
调用存储过程的参数可以使用参数占位符.给存储过程的in参数传入值时,使用CallableStatement对象的set方法(例如pState.setString(1,"abc"))。给存储过程的out参数传入值时,使用CallabelStatement对象的registerOutParameter()方法向CallableStatement对象注册out参数,执行完存储过程之后,调用get方法获取返回的out参数。
JDBC API对调用的存储过程进行了转移,转义后对所有关系行数据库都能使用统一的方式调用存储过程,兼容所有数据库。义语法有一个包含结果参数的形式和一个不包含结果参数的形式。
包含结果参数的:{?= call <procedure-name>[(<arg1>,<arg2>, ...)]},我在开发中使用的Mysql数据库,Mysql数据库的存储过程应该不能返回值(return 值),返回的值都是通过out参数带出,或者是使用select语句返回结果集。不知道其它数据库能不能返回一个值。
不包含结果参数的:{call <procedure-name>[(<arg1>,<arg2>, ...)]}
执行数据库存储过程代码事例
package com.zhangxy.jdbcAPITest;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* 利用CallableStatement调用数据库存储过程
* @author Administrator
*
*/
public class CallableStatementTest {
/**
* 调用带输入参数和输出参数的存储过程
DROP PROCEDURE IF EXISTS `getsum`;
DELIMITER ;;
CREATE DEFINER = `root`@`localhost` PROCEDURE `getsum`(IN s1 int, IN s2 int, OUT r int)
BEGIN
set r = s1+s2;
END
;;
DELIMITER
*/
public static void callOne(){
Connection conn = null;
try {
conn = DBCPPool.getConnection();
String sql="{call getsum(?,?,?)}";
CallableStatement cState = conn.prepareCall(sql);
cState.setInt(1, 100);
cState.setInt(2, 200);
cState.registerOutParameter(3, java.sql.Types.INTEGER);
cState.executeUpdate();
int result = cState.getInt(3);
System.out.println("调用 getsum 存储过程结果为:"+result);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
if(conn!=null){
try {
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
/**
* 调用返回结果集的存储过程
*
DROP PROCEDURE IF EXISTS `getstudent`;
DELIMITER ;;
CREATE DEFINER = `root`@`localhost` PROCEDURE `getstudent`(IN no int)
BEGIN
select * from student t where t.no=no;
END
;;
DELIMITER ;
*/
public static void call2(){
Connection conn = null;
try {
conn = C3P0Pool.getConnection();
CallableStatement pState = conn.prepareCall("{call getstudent(?)}");
pState.setInt(1, 120000);
ResultSet rs = pState.executeQuery();
System.out.println("调用 getstudent 存储过程的结果为:");
while(rs.next()){
System.out.println("name:"+rs.getString("name"));
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
if(conn!=null){
try {
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
/**
* 调用数据库函数 hello
DROP FUNCTION IF EXISTS `hello`;
CREATE DEFINER = `root`@`localhost` FUNCTION `hello`(s CHAR(20))
RETURNS char(50)
RETURN CONCAT('Hello, ',s,'!');
*/
public static void callFunction(){
Connection conn = null;
try {
conn = C3P0Pool.getConnection();
PreparedStatement pState = conn.prepareStatement("select hello(?) as v");
pState.setString(1, "abc");
ResultSet rs = pState.executeQuery();
while(rs.next()){
System.out.println("调用数据库函数 hello 返回结果:"+rs.getString("v"));
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void main(String [] args){
callOne();
call2();
callFunction();
}
}
java.sql.ResultSetMetaData,(20170807补充)
ResultSetMetaData类可用于获取ResultSet对象中列的类型和属性信息对象。我们可以通过rs.getMetaData()对象来获取ResultSetMetaData对象。其实能去了解这个类是因为对一个问题的思考:我们使用java的ORM框架时,都需要创建一个对应的JavaBean来映射表的结构,发现myBatis从数据库中返回的可以不是一个JavaBean,而是一个Map或JSONObject,这这个两个对象的Key值都是和表字段名称一样的。感觉JDBC应该提供了响应的API来读取ResultSet对象列类型和属性的类,结果顺藤摸瓜就找到了ResultSetDetaData类。下面介绍一下此类常用的API。
- 获取指定位置的列所在表的名称。column参数为列所在的位置。注意这个位置是从1开始,而不是0。ResultSetMateData对象的所有方法参数列的位置都是从1开始。
String getTableName(int column) throws SQLException
- 返回ResultSet中的列数。
int getColumnCount() throws SQLException
- 按照指定的位置获取列的名称。
String getColumnName(int column) throws SQLException
- 按照指定的位置返回列在数据库中的类型。
String getColumnTypeName(int column) throws SQLException
- 按照指定的位置返回列对应的java.sql.Types类中的类型。
int getColumnType(int column) throws SQLException
- 按照指定的位置返回列对应java编程语言中类的完全限定名称。
String getColumnClassName(int column) throws SQLException
- 指定列在表结构上是否可以为null。
int isNullable(int column) throws SQLException
- 按照指定列的位置返回该列的别名,通常由as指定。如果没有指定别名,此方法的返回值将和getColumnName()方法相同。
String getColumnLabel(int column) throws SQLException
下面给出一个综合事例:
public static void testResultSetMateData(Connection conn) {
String sql = "select CONVERT(NO,SIGNED) no, NAME from student s where 1=2";
//String sql = "select t.act_id from dbmarketadm.mk_actrecord_info t where t.act_id in ('201707285002064064')";
Statement smt = null;
ResultSet rs = null;
java.sql.ResultSetMetaData rsmd = null;
try {
smt = conn.createStatement();
rs = smt.executeQuery(sql);
conn.getMetaData();
rsmd = rs.getMetaData();
String tableName = rsmd.getTableName(2);
int columnCount = rsmd.getColumnCount();
System.out.println("表名:" + tableName + ",查询结果为:" + columnCount);
for (int i = 1; i <= columnCount; i++) {
System.out.println("列名称:" + rsmd.getColumnName(i) + ",数据库类型:" + rsmd.getColumnTypeName(i) + ",sql数据库类型:"
+ rsmd.getColumnType(i) + ",列别名(as):" + rsmd.getColumnLabel(i) + ",列是否为null:"
+ rsmd.isNullable(i) + ",列类型对于的java类型:" + rsmd.getColumnClassName(i));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (smt != null) {
try {
smt.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
输出结果:
表名:student,查询结果为:2
列名称:no,数据库类型:BIGINT,sql数据库类型:-5,列别名(as):no,列是否为null:0,列类型对于的java类型:java.lang.Long
列名称:name,数据库类型:VARCHAR,sql数据库类型:12,列别名(as):NAME,列是否为null:1,列类型对于的java类型:java.lang.String