JDBC学习笔记二
jdbc学习笔记二
4.execute()方法执行sql语句
execute几乎可以执行任何sql语句,当execute执行过sql语句之后会返回一个布尔类型的值,代表是否返回了resultset对象。
以下两个方法分别获得结果集或受影响的行数:
getresultset(): 获取该statement执行查询语句返回的resultset对象 getupdatecount(): 获取该statement执行dml语句所影响的记录行数
部分示例代码:
//stmt即为connection对象创建的statement boolean hasresult = stmt.execute(sql); if(hasresult){ //获取结果集 resultset rs = stmt.getresultset; } else{ //获取受影响的行数 int count = stmt.getupdatecount' }
5.preparedstatement
perparedstatement时statement的子接口,允许使用?
占位符来代替具体值,在通过connection对象创建preparedstatement对象时需要传入sql语句来进行预编译:
preparedstatement pstmt = conn.preparestatement("允许带占位符的sql语句");
perparedstatement在执行sql语句之前,要通过之前提到过的setxxx(int parameterindex, xxx value)方法来对占位符进行赋值。执行语句时的方法跟statement方法相同,只是不需要传入sql语句。
preparedstatement还可以防止sql注入,statement如果要使用变量就需要拼接字符串,所以存在sql注入的风险。
测试代码(使用之前的工具类以及junit单元测试):
@test public void pstmttest() throws exception{ string sql = "select * from test where one > ?"; connection conn = jdbc2.getconnection(); preparedstatement pstmt = conn.preparestatement(sql); //给占位符赋值 pstmt.setint(1, 1); //执行sql语句 resultset rs = pstmt.executequery(); while(rs.next()){ system.out.println(rs.getint(1) + " " + rs.getstring(2) + " " + rs.getstring(3)); } //关闭连接 jdbc2.close(conn, pstmt, rs); }
结果测试成功,这里就不再贴出
6.callablestatement
调用存储过程使用callablestatement,通过connection的preparecall方法来创建callablestatement对象,创建该对象时需要传入调用存储过程的sql语句,示例如下:
callabestatement cstmt = conn.preparecall("call 存储过程名(?, ?, ?)");
可以通过callablestatement的setxxx(int indix, xxx valie)方法为传入参数设置值,传出参数需要调用registeroutparameter方法来注册参数,示例如下:
cstmt.registeroutparameter(int index, types.数据类型)
简单示例:
sql中的存储过程
create procedure simple_test(in a int ,in b int, out sum int) begin set sum = a + b; end;
java代码
@test public void cstmttest()throws exception{ connection conn = jdbc2.getconnection(); //创建callablestatement对象 callablestatement cstmt = conn.preparecall("call simple_test(?, ?, ?)"); //为传入参数设置值 cstmt.setint(1, 6); cstmt.setint(2, 2); //注册参数 cstmt.registeroutparameter(3,types.integer); cstmt.execute(); system.out.println(cstmt.getint(3)); }
测试结果为8,这里就不再贴出
7.resultset
resultset为结果集,其中包含一个指针指向结果集中的某一行,可以利用内置的各种方法来移动指针,常用方法在之前的2.5中已经给出。
在jdk1.4之前的版本中,默认打开的resultset是不可滚动的,必须在创建statement过prestatement时需要传入额外参数,jdk1.5以后默认打开了resultset就是可滚动的,无需传入额外参数。
传入的参数有以下两种类型:
resultsettype:控制resultset的类型,该参数可以是三个值: resultset.type_forward_only: 该常量控制记录指针只能向前移动 resultset.type_scroll_insensitive:可滚动结果集,但底层数据的改变不会影响resultset的值 resultset.type_scroll_sensitive:可滚动结果集,底层数据的改变会影响resultset的值 resultsetconcurrency:控制resultset的并发类型,该参数可以是以下两个值: resultset.concur_read_only:该常量指示resultset是只读的并发模式(默认) resultset.concur_updatable:该常量指示resultset是可更新的并发模式
如果要创建可更新的结果集,查询语句查询的数据通常只能来自于同一个数据表,而且查询结果集中的数据列必须包含主键列,否则将会引起更新失败。
示例代码:
@test public void rstest() throws exception { connection conn = jdbc2.getconnection(); //创建可更新可滚动的resultset对象 preparedstatement pstmt = conn .preparestatement("select * from test where one > ?", resultset.type_scroll_insensitive, resultset.concur_updatable); //给占位符赋值 pstmt.setint(1, 1); //执行sql语句 resultset rs = pstmt.executequery(); //移动到最后 rs.last(); //获取指针当前所在的行数 int rowcount = rs.getrow(); system.out.println("修改前从后往前遍历:"); for(int i = rowcount; i > 0; i--){ //移动到第i行 rs.absolute(i); system.out.println(rs.getint(1) + " " + rs.getstring(2) + " " + rs.getstring(3)); //修改指针所在行的第二列的记录 rs.updatestring(2, "修改后的"+ i); //提交修改 rs.updaterow(); } //正序输出修改后的数据 system.out.println("修改后从前往后遍历"); for(int i = 1; i <= rowcount; i++){ rs.absolute(i); system.out.println(rs.getint(1) + " " + rs.getstring(2) + " " + rs.getstring(3)); } jdbc2.close(conn, pstmt, rs); } }
数据库表中的数据也会随之更改。
8.resultsetmetadata
metadata的意思是元数据,即描述其他数据的数据,resultsetmetadata封装了描述resultset对象的数据。
根据result的getmetadate()方法来获取resultsetmetadata对象
常用方法如下:
int getcolumncount(): 返回该resultset的列数量 string getcolumnname(int column): 返回指定索引的列名 int getcolumntype(int column): 返回指定索引的列类型
使用过resultsetmetadata对象后也要释放资源
9.事务
常用方法在中已经在2.2中写出
下面就贴一下简单的示例代码:
@test public void transaction() throws exception{ connection conn = jdbc2.getconnection(); //关闭自动提交,开启事务 conn.setautocommit(false); statement stmt = conn.createstatement(); //执行sql语句 stmt.executeupdate("insert into test values(4, '测试commit4', '测试commit4')"); //设置回滚点 //savepoint savepoint1 = conn.setsavepoint("savepoint1"); stmt.executeupdate("insert into test values(5, '测试commit5', '测试commit5')"); //回滚事务,不填写回滚点的话则撤销之前所有操作 //conn.rollback(savepoint1); //提交事务(回滚之后也要提交) //conn.commit(); jdbc2.close(conn, stmt, null); }
10.批量更新
多条sql语句将会被作为一批操作被同时收集,并同时提交。
使用批量更新需要先创建一个statement对象,然后使用该对象的addbatch方法将多条sql语句同时收集起来,最后调用statement对象的executebatch同时执行这些sql语句,如下所示:
statement stmt = conn.createstatement(); //收集多条sql语句 stmt.addbatch(sql1); stmt.addbatch(sql2); stmt.addbatch(sql3); ... //同时执行所有sql语句 stmt.executebatch();
执行executebatch()方法将返回一个int[]数组,返回每次执行的所影响的行数,因此如果addbatch()时添加查询语句运行时会出现错误。因此可以在执行批处理前开启事务,当出现错误时回滚到初始状态。
11.连接池
数据库的连接及关闭非常耗费系统资源,为了减少资源浪费,提高程序运行效率,可以采用数据库连接池。
数据库连接池:当程序启动时,系统自动建立足够的数据库连接,并将这些连接组成一个连接池。每次应用程序请求数据库连接时,无须重新打开连接,而是从池中取出已有的连接使用,当使用完后,不再关闭数据库连接,而是将连接归还给连接池。连接池就类似于之前所学的对象工厂(也是在程序开始运行时,将properties文件中的类实例化)
c3p0和dbcp对比:
dbcp | c3p0 | |
---|---|---|
对数据连接处理的方式 | 提供最大连接数 | 提供最大空闲时间 |
什么时候挂断 | 当连接超过最大连接数 | 自动回收连接 |
连接资源是否释放 | 需要自己手动释放资源 | 需要自己手动释放资源 |
效率 | 效率比较高 | 效率没有dbcp高 |
推荐使用 | spring开发 | hibernate开发 |
11.1.dbcp
需要jar包:common-dbcp
,common-pool
, common-collections
maven依赖:
<dependency> <groupid>org.apache.commons</groupid> <artifactid>commons-dbcp2</artifactid> <version>2.1.1</version> </dependency> <dependency> <groupid>org.apache.commons</groupid> <artifactid>commons-pool2</artifactid> <version>2.6.1</version> </dependency> <dependency> <groupid>org.apache.commons</groupid> <artifactid>commons-collections4</artifactid> <version>4.1</version> </dependency>
简单模板:
import org.apache.commons.dbcp2.basicdatasource; import java.io.fileinputstream; import java.io.ioexception; import java.sql.connection; import java.sql.sqlexception; import java.util.properties; public class dbcp { private static basicdatasource ds; static{ try{ ds = new basicdatasource(); //读取配置文件 properties prop = new properties(); prop.load(new fileinputstream("src/main/resources/datasource.properties")); //设置驱动,高版本jar包不需要这一步,如果写的话写成“com.mysql.jdbc.driver” //ds.setdriverclassname(prop.getproperty("driver")); //设置url ds.seturl(prop.getproperty("url")); //用户名 ds.setusername(prop.getproperty("username")); //密码 ds.setpassword(prop.getproperty("password")); //设置连接池初始连接数 ds.setinitialsize(integer.parseint(prop.getproperty("initialsize"))); //设置最大活动连接数 ds.setmaxtotal(integer.parseint(prop.getproperty("maxactive"))); //设置最小空闲连接 ds.setminidle(integer.parseint(prop.getproperty("minidle"))); }catch (ioexception e){ e.printstacktrace(); } system.out.println("finish init"); } public static connection getconnetion(){ try { return ds.getconnection(); }catch (sqlexception e){ e.printstacktrace(); return null; } } }
上面的属性配置只是常用配置,完整的配置请参考官方文档。
测试代码:
@test public void dbcptest() { connection conn = null; statement stmt = null; resultset rs = null; try{ conn = dbcp.getconnetion(); stmt = conn.createstatement(); rs = stmt.executequery("select * from test"); while(rs.next()) system.out.println(rs.getint(1) + " " + rs.getstring(2) + " " + rs.getstring(3)); }catch (sqlexception e){ e.printstacktrace(); } finally { //关闭连接 try { if(rs != null) rs.close(); if(stmt != null) stmt.close(); if(conn != null) conn.close(); }catch (sqlexception e){ e.printstacktrace(); } } }
11.2.c3p0
需要jar包:c3p0
;
maven依赖:
<dependency> <groupid>com.mchange</groupid> <artifactid>c3p0</artifactid> <version>0.9.5.2</version> </dependency>
简单模板:
将dbcp中的basicdatasource替换为combopooleddatasource
驱动,url,用户名和密码设置方式相同,剩余的请参考官方文档