文章目录
一、最简单的流程
- 加载相应的数据库驱动
- 通过DriverManger获取Connection
- 通过Connection获取PrepareStatement,并设置查询语句
- 通过PrepareStatement执行sql语句
- 通过ResultSet获取查询结果
例子:
import java.sql.*;
public class Main {
public static void main(String[] args) {
System.out.println("打印出test数据库中的user表数据");
//1.加载数据库驱动
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
System.out.println("没有找到mysql数据库驱动!!!");
e.printStackTrace();
System.exit(0);
}
//2.获取连接
Connection conn;
try {
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?useSSL=false", "root", "root");
//3.获取PrepareStatement对象,并传入查询语句
PreparedStatement ps = conn.prepareStatement("SELECT * FROM user ");
//4.执行PrepareStatement对象
ResultSet resultSet = ps.executeQuery();
//5.用游标输出结果
while (resultSet.next()) {
System.out.print("id:" + resultSet.getInt(1));
System.out.print(" username:" + resultSet.getString(2));
System.out.println(" age:" + resultSet.getInt(3));
}
} catch (SQLException e) {
e.printStackTrace();
System.exit(0);
}
}
}
运行结果:
二、常用接口(还真是Interface)
接口 | 说明 |
---|---|
DriverManger | 数据库驱动程序,用于连接jdbc和对应数据库 |
Connection | 用于创建一个数据库连接,要想访问数据库,就得先获取数据库连接。通过DrverManger.getConnection()获取连接 |
PrepareStatement | 预编译的Statement对象,只有更改sql参数就行。通过ConnectionInstance.prepareStatement()获取。 |
ResultSet | 结果集对象。可以同PrepareStatementInstance.executeQuery()返回 |
三、执行SQL语句的几种方式
方式 | 说明 |
---|---|
execute() | 此方法的返回值是boolean,表示是否返回ResulSet对象。若是,可以通过getResultSet()获取。 |
executeUpate() | 返回受影响的行数。 |
executeLargeUpdate() | 和executeUpdate()差不多,只是当受影响的数目非常大时(推荐使用executeLargerUpdate()) |
executeQuery() | 只能执行查询语句,返回ResultSet |
上面方法时Statement的,当然PrepareStatement也提供了这些方法。但使用Statement和PrepareStatement的区别:
- PrepareStatement预编译SQL语句,性能更好
- PrepareStatement不用拼接sql语句,编程更简单
- PrepareStatement可以防止SQL注入(2就是原因),更safe。
使用CallableStatement可以调用存储过程,可以通过conn.prepareCall(格式)获取:
调用格式是{call 过程名(?,?,?)}。其中?表示占位符。
‘?’表示的参数可以是传入参数,也可以是传出参数:传入参数:通过setXXX(position,value)为参数设置
传出参数:需要registerOutParmter(position,TYPE)来注册
例子(我已经通过navicat创建了一个存储过程):
/**
* 使用CallableStatement调用存储过程
*/
public static void callableStatementTest(){
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try {
Connection conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/test?useSSL=false","root","root");
CallableStatement cs=conn.prepareCall("{call add_pro(?,?,?)}");
cs.setInt(1,1);
cs.setInt(2,2);
cs.registerOutParameter(3,Types.INTEGER);
cs.execute();
System.out.println(cs.getInt(3));
} catch (SQLException e) {
e.printStackTrace();
}
}
在main函数调用后的运行结果:
3
四、ResultSet管理结果集
4.1 可滚动、可更新的结果集
可滚动是指游标可以定位到任意位置(通过absolute()、previous()等方法)。我们默认创建的ResultSet是不可以修改的。所以要创建可滚动、可更新的结果集方法:
- 在获取PrepareStatement或Statement时,要传入参数(TYPE_SCROLL_INSENSTIVE,CONCUR_UPDATEABLE,其他参数我没写)。例子
- 在更改ResultSet后(ResultSet提供了updateXXX(index,value)修改结果集),要调用updateRow()方法
4.2 处理Blob类型(二进制)数据
Blob类型使用来存储图片、声音等大型文件的。虽然不推荐直接将图片、声音等大型文件直接存在数据库,但是数据库任然提供了存储这些大型文件的方法。
- PrepareStatement提供了setBinaryStrem(int parameterIndex,InputStream in)方法来存储二进制文件
- ResultSet提供了getBlob(int index)方法获取Blob对象,Blob对象提供了getBinaryStream()方法获取输出流。Blob对象也提供了getBytes()方法获取对应的二进制数组。
4.3 通过ResultSetMetaData分析结果集
ResultSet提供了getResultSetMetaData()方法来获取ResultSetMetaData对象,有了它,我们就可以获取ResultSet的描述信息了。ResultSetMetaData的常用方法:
需要注意的是使用ResultSetMetaData需要一定的系统开销。
五、RowSet管理结国集和分析结果集
5.1 什么是RowSet?
个人认为RowSet是对ResultSet的一种改善。它默认是可滚动的、可更新的可序列化的结果集。对于离线RowSet来说,创建时已经将数据读入内存,所以无需与数据库保持连接,从而降低了数据库的负载,提高了性能。RowSet规范的接口类图如下:
5.2 怎样创建RowSet?
老版(不推荐使用):
通过JdbcRowSetImpl,CacheRowSetImpl,WebRowSetImpl,JoinRowSet,FilterRowSet创建。
新版(RowSetProvider、RowSetFactory):
5.3 一个例子离线RowSet及离线RowSet的查询分页
public static void main(String[] args) throws SQLException, ClassNotFoundException {
CachedRowSet crs=simpleStep();
//对crs进行操作
while(crs.next()){
System.out.println("id:"+crs.getInt(1)+" username:"+crs.getString(2)+" age:"+crs.getInt(3));
//修改年龄,年龄加1
crs.updateInt(3,crs.getInt(3)+1);
crs.updateRow();
}
//重新获取连接,完成更新
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?useSSL=false", "root", "root");
conn.setAutoCommit(false);
crs.acceptChanges(conn);
}
public static CachedRowSet simpleStep(){
System.out.println("打印出test数据库中的user表数据");
//1.加载数据库驱动
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
System.out.println("没有找到mysql数据库驱动!!!");
e.printStackTrace();
System.exit(0);
}
//2.获取连接
Connection conn;
try {
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?useSSL=false", "root", "root");
//3.获取PrepareStatement对象,并传入查询语句
PreparedStatement ps = conn.prepareStatement("SELECT * FROM user ");
//4.执行PrepareStatement对象
ResultSet resultSet = ps.executeQuery();
//通过离线RowSet存
RowSetFactory rsf= RowSetProvider.newFactory();
CachedRowSet crs=rsf.createCachedRowSet();
//5.用游标输出结果
/*while (resultSet.next()) {
System.out.print("id:" + resultSet.getInt(1));
System.out.print(" username:" + resultSet.getString(2));
System.out.println(" age:" + resultSet.getInt(3));
}*/
crs.populate(resultSet);//将resultSet封装起来
return crs;
} catch (SQLException e) {
e.printStackTrace();
System.exit(0);
}
return null;
}
运行结果:
注意:populate进行封装时,默认应该是有一个开始位置的!!!!
分页查询,只是在上面代码基础上进行简单的修改。为什么要进行分页查询?是因为CacheRowSet是保存在内存中的,当记录数太多时会占有很大内存,所以进行分页查询。
public static void main(String[] args) throws SQLException, ClassNotFoundException {
CachedRowSet crs=simpleStep(1,2);
//对crs进行操作
System.out.println( crs.absolute(2));
System.out.println(crs.absolute(1));
while(crs.next()){
System.out.println("id:"+crs.getInt(1)+" username:"+crs.getString(2)+" age:"+crs.getInt(3));
//修改年龄,年龄加1
crs.updateInt(3,crs.getInt(3)+1);
crs.updateRow();
}
//重新获取连接,完成更新
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?useSSL=false", "root", "root");
conn.setAutoCommit(false);
crs.acceptChanges(conn);
}
public static CachedRowSet simpleStep(int pageIndex,int pageSize){
System.out.println("打印出test数据库中的user表数据");
//1.加载数据库驱动
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
System.out.println("没有找到mysql数据库驱动!!!");
e.printStackTrace();
System.exit(0);
}
//2.获取连接
Connection conn;
try {
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?useSSL=false", "root", "root");
//3.获取PrepareStatement对象,并传入查询语句
PreparedStatement ps = conn.prepareStatement("SELECT * FROM user ");
//4.执行PrepareStatement对象
ResultSet resultSet = ps.executeQuery();
//通过离线RowSet存
RowSetFactory rsf= RowSetProvider.newFactory();
CachedRowSet crs=rsf.createCachedRowSet();
crs.setPageSize(pageSize);
crs.populate(resultSet,(pageIndex-1)*pageSize+1);
return crs;
} catch (SQLException e) {
e.printStackTrace();
System.exit(0);
}
return null;
}
运行结果:
六、事务管理与批量更新
6.1 mysql的事务支持
事务提交的方式:
自动提交是和开启事务相反的,开启事务就关闭了自动提交,开启自动提交就关闭了事务。
事务回滚方式:
如果只想暂时性开启事务,可以使用start transaction或begin两个命令。
还有,mysql 提供了 savepoint 来设置事务中间点,这样rollback时就可以选择回到中间点了。
6.2 jdbc的事物支持
Connection默认是打开自动提交,关闭事务的。所以可以通过==setAutoCommit()==来关闭自动提交,打开事务。
提交事务使用conn.commit()。
事务回滚使用conn.rollback()。
Connection也提供了设置中间点的方法setSavepoint(),当然也提供了相应的回滚方法rollback(savapoin)。
6.3 批量更新
Statement或PrepareStatement提供了addBatch(sql),executeLargeBatch()方法来支持批量更新
分析数据库信息
可以通过 DatabaseMetaData 来分析数据库信息,DatabaseMetaData 以ResultSet来返回查询信息。
还可以通过 系统表 来分析数据库。
使用连接池管理连接
为什么要使用连接池管理连接?是因为单独每一次连接建立、关闭都很耗费资源,对系统性能影响尤为明显。
数据库连接池的常用参数:
可以使用DBCP,C3P0数据源1来管理连接。
DataSource称为数据源,它包括连接池和连接池管理两部分。但习惯上把DataSource称为连接池。 ↩︎