文章目录
一、JDBC是什么?
JDBC-Java Database Connectivity-Java数据库连接
二、为什么要使用JDBC?
可以使用相同的API实现连接、操作不同的数据库
三、DDL、DML、DCL分别代表什么
DDL(Data Definition Language 数据定义语言)语句:主要由create,alter,drop和truncate四个关键字完成
DML(Data Manipulation Language 数据操作语言)语句:主要由insert,update和delete三个关键字完成
DCL(Data Control Language 数据控制语言) 语句:主要由grant和revoke两个关键字组成
事务性语言:主要由commit,rollback和savepoint三个关键字组成
四、JDBC有三个操作步骤
建立与数据库的连接、执行SQL语句、获得SQL语句执行结果
- 加载驱动,规范
Class.forName(dirver) - 使用Connection创建Connection对象,以便可以使用Connection创建Statement对象
Connection conn = DriverManager.getConnection(url,user,pass) - 使用Connection对象创建Statement对象,以便使用Statement对象的executeXxx方法
Statement stmt = conn.createStatement() - 调用Statement对象的executeUpdate()、executeQuery()、execute()方法
executeUpdate():用于执行DML语句,返回一个整数
实例代码
/**
* @ClassName: ExecutionDDLTest
* @description: 本节代码主要实现使用executeLargeUpdate()来执行DDL语句
* DDL语言:主要由create,drop,alter,truncate
* @author: FFIDEAL
* @Date: 2020年4月5日 下午9:28:59
*/
package M13;
import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
import java.util.Properties;
import com.mysql.cj.jdbc.Driver;
public class ExecutionDDLTest {
private String driver;
private String url;
private String user;
private String pass;
public void initParam(String paramFile) throws Exception{
//使用Properties类来加载属性文件
Properties props = new Properties();
props.load(new FileInputStream(paramFile));
driver = props.getProperty("driver");
url = props.getProperty("url");
user = props.getProperty("user");
pass = props.getProperty("pass");
}
public void createTable(String sql) throws Exception{
//加载驱动
Class.forName(driver);
try(
//连接数据库
Connection conn = DriverManager.getConnection(url, user, pass);
//使用Connection来创建一个Statement对象
Statement stmt = conn.createStatement()
){
//执行DDL语句,创建数据表
stmt.executeUpdate(sql);
}
}
public static void main(String[] args) throws Exception{
ExecutionDDLTest ed = new ExecutionDDLTest();
ed.initParam("E:\\JavaCode\\Code\\src\\M13\\mysql.ini");
ed.createTable("create table jdbc_test ("
+ "jdbc_id int auto_increment primary key,"
+ "jdbc_name varchar(255),"
+ "jdbc_desc text"
+ ");");
System.out.println("=======表创建完成======");
}
}
executeQuery():执行select语句,查询到的结果
实例代码
/**
* @ClassName: ConnMySql
* @description: 本节代码简单示范了JDBC编程,并通过ResultSet获得结果集的过程
* @author: FFIDEAL
* @Date: 2020年4月4日 下午8:22:06
*/
package M13;
import java.security.interfaces.RSAKey;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
public class ConnMySql {
public static void main(String[] args) throws Exception{
//1.加载驱动,使用反射知识,现在就记住
/* 5版本是Class.forName("com.mysql.jdbc.Driver");
* 8版本是Class.forName("com.mysql.cj.jdbc.Driver");
*/
Class.forName("com.mysql.cj.jdbc.Driver");
try (
/*2.使用DriverManager获取数据库连接
* 其中返回的Connection就代表了Java程序和数据库的连接
* 不同数据库URL写法需要查驱动文档,用户名、密码有DBA分配
*/
Connection conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/crazyjavajdbc?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC", "root","root");
//3.使用Connection来创建一个Statement对象
Statement stmt = conn.createStatement();
/*4.执行SQL语句
* Statement有三种执行SQL语句的方法:
* ①.execute()可执行任何SQL语句-返回一个boolean值,如果执行后第一个结果是ResultSet就返回true,否则返回false
* ②.executeQuery()执行select语句 - 返回查询到的结果集
* ③.executeUpdate()用于执行DML语句-返回一个整数,代表被SQL语句影响的记录条数
*/
// ResultSet rs = stmt.executeQuery("select s.* , teacher_name from student_table s,teacher_table t where s.java_teacher = t.teacher_id")
ResultSet rs = stmt.executeQuery("select s.* , t.teacher_name "
+ "from student_table s , teacher_table t "
+ "where s.java_teacher = t.teacher_id")
)
{
/*ResultSet有一系列的getXxx(列索引|列名)方法们勇于获取指针记录
* 指向航、特定列的值,不断的使用next()将记录指针以下一行
* 若移动指针又有效,则next()返回true
*/
while(rs.next()) {
System.out.println(rs.getInt(1)+"\t"
+rs.getString(2)+"\t"
+rs.getString(3)+"\t"
+rs.getString(4));
}
}
}
}
execute():可以执行任何一个SQL语句,返回一个boolean
但是可以执行getResultSet():获得该Statement执行查询语句所返回的ResultSet对象
getUpdateCount():获取该Statement执行的DML语句所影响的记录条数
实例代码
/**
* @ClassName: ExecuteSQL
* @description: 本机代码主要讨论使用execute()方法来执行SQL语句
* execute()方法的有点:几乎可以执行任何的SQL语句,而不用考虑DML(executeUpdate)还是DDL()
* execute():可以执行任何一个SQL语句,返回一个boolean,
* 但是可以执行
* getResultSet():获得该Statement执行查询语句所返回的ResultSet对象
* getUpdateCount():获取该Statement执行的DML语句所影响的记录条数
* @author: FFIDEAL
* @Date: 2020年4月6日 下午2:30:58
*/
package M13;
import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Properties;
public class ExecuteSQL {
private String driver;
private String url;
private String user;
private String pass;
public void initParam(String paramFile) throws Exception{
Properties props = new Properties();
props.load(new FileInputStream(paramFile));
driver = props.getProperty("driver");
url = props.getProperty("url");
user = props.getProperty("user");
pass = props.getProperty("pass");
}
public void executeSQL(String sql) throws Exception{
Class.forName(driver);
try(
Connection conn = DriverManager.getConnection(url, user, pass);
Statement stmt = conn.createStatement();
){
//执行SQL语句,返回boolean值表示是否包含ResultSet
boolean hasResultSet = stmt.execute(sql);
if(hasResultSet) {
try(
//获取结果集
ResultSet rs = stmt.getResultSet();
){
//ResultSetMetaData是用于分析结果集合的元数据接口
java.sql.ResultSetMetaData rsmd = rs.getMetaData();
int columnCount = rsmd.getColumnCount();
//迭代输出ResultSet结果集
while(rs.next()) {
for (int i = 0; i < columnCount ; i++) {
System.out.print(rs.getString( i + 1 ) + "\t");
}
System.out.println("\n");
}
}
}
}
}
public static void main(String[] args) throws Exception {
ExecuteSQL es = new ExecuteSQL();
es.initParam("E:\\JavaCode\\Code\\src\\M13\\mysql.ini");
System.out.println("=====执行删除表的DDL=====");
es.executeSQL("drop table if exists my_test");
System.out.println("=====执行新建表的DDl语句=====");
es.executeSQL("create table my_test"
+ "(test_id int auto_increment primary key, "
+ "test_name varchar(255));");
System.out.println("=====执行插入数据的DML语句======");
es.executeSQL("insert into my_test(test_name) select student_name from student_table");
es.executeSQL("insert into my_test values(3,'小红');");
System.out.println("=====执行查询数据的查询语句=====");
es.executeSQL("select * from my_test");
}
}
五、使用PreparedStatement执行SQL语句
PreparedStatement接口可以允许在SQL中使用占位符(?),这是在Statement接口中中不被允许的。
PreparedStatement的效率比Statement高,这是因为可以使用PreparedStatement对象多次高效的执行SQL语句。使用办法和Statement方法类似,且提供的方法也是类似的,有execute()方法、executeUpdate()方法和executeQuery()方法等
除了效率较高以外,PreparedStatement还有一个优势,就是使用PreparedStatement不需要拼接SQL语句,见一下代码的两个方法中的SQL语句。
除了上述两个优点以外,使用PreparedStatement可以防止SQL注入(常见的利用SQL语句漏洞来入侵的方法)
PreparedStatement ps = conn.preparedStatement("insert into student_table values(null,?,1)");
实例代码
/**
* @ClassName: PreparedStatementTest
* @description: 本节代码主要讨论PreparedStatement和Statement性能的对比
* @author: FFIDEAL
* @Date: 2020年4月6日 下午6:01:24
*/
package M13;
import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.Statement;
import java.util.Properties;
public class PreparedStatementTest {
private String driver;
private String url;
private String user;
private String pass;
public void initParam(String paramFile) throws Exception{
Properties props = new Properties();
props.load(new FileInputStream(paramFile));
driver = props.getProperty("driver");
url = props.getProperty("url");
user = props.getProperty("user");
pass = props.getProperty("pass");
Class.forName(driver);
}
public void insertUseStatement() throws Exception{
long start = System.currentTimeMillis();
try(
Connection conn = DriverManager.getConnection(url, user, pass);
Statement stmt = conn.createStatement();
){
for (int i = 0; i < 100; i++) {
stmt.executeUpdate(
"insert into student_table values(null,'姓名" + i + "',1)");
}
System.out.println(
"使用Statement费时:" + (System.currentTimeMillis() - start));
}
}
public void insertUsePreparedStatement() throws Exception{
long start = System.currentTimeMillis();
try(
Connection conn = DriverManager.getConnection(url, user, pass);
PreparedStatement ps = conn.prepareStatement(
"insert into student_table values(null,1,?)");
){
//100次为PreparedStatement的参数设置,可以插入100条记录
for (int i = 0; i < 100; i++) {
ps.setString(1, "姓名" + i);
ps.setInt(1, i);
ps.executeUpdate();
}
System.out.println("使用PreparedStatement费时:"
+ (System.currentTimeMillis() - start));
}
}
//使用execute()方法来输出查询结果
public void selectAll() throws Exception{
try(Connection conn = DriverManager.getConnection(url, user, pass);
Statement stmt = conn.createStatement();
){
boolean hasResultSet = stmt.execute("select * from student_table");
if(hasResultSet) {
try(
//通过getResultSet方法获得结果集
ResultSet rs = stmt.getResultSet();
){
//获得变化的数字
ResultSetMetaData rsmd = rs.getMetaData();
int col = rsmd.getColumnCount();
while(rs.next()) {
for (int i = 0; i < col; i++) {
System.out.print(rs.getString(i + 1) + "\t");
}
System.out.println("\n");
}
}
}
}
}
public static void main(String[] args) throws Exception{
PreparedStatementTest pst = new PreparedStatementTest();
pst.initParam("E:\\JavaCode\\Code\\src\\M13\\mysql.ini");
pst.insertUseStatement();
pst.insertUsePreparedStatement();
pst.selectAll();
}
}
六、使用CallableStatement调用存储过程
前提是要在数据库里有这个事务
//使用Connection来创建一个CallableStatement对象
cstmt = conn.prepareCall("{call add_pro(?,?,?)}");
//传入参数可以用setXxx(index, number);
//使用registerOutParameter()方法来注册参数
//之后用execute()来执行
//getXxx(int index)来获得参数
实例代码
/**
* @ClassName: CallableStatemnetTest
* @description: 本节代码主要讨论CallableStatement调用存储过程
* @author: FFIDEAL
* @Date: 2020年4月6日 下午8:51:05
*/
package M13;
import java.io.FileInputStream;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Types;
import java.util.Properties;
public class CallableStatemnetTest {
private String driver;
private String url;
private String user;
private String pass;
public void initParam(String paramFile) throws Exception{
Properties props = new Properties();
props.load(new FileInputStream(paramFile));
driver = props.getProperty("driver");
url = props.getProperty("url");
user = props.getProperty("user");
pass = props.getProperty("pass");
Class.forName(driver);
}
public void CallProcedure() throws Exception{
try(
Connection conn = DriverManager.getConnection(url, user, pass);
//通过Connection对象创建一个CallableStatement对象,"{call 存储过程名称(占位符参数1,占位符参数2,……)}"
CallableStatement cs = conn.prepareCall("{call add_pro(?,?,?)}");
){
cs.setInt(1, 2);
cs.setInt(2, 3);
//输出int类型
cs.registerOutParameter(3, Types.INTEGER);
//执行程序
cs.execute();
//输出结果
System.out.println("执行结果是:" + cs.getInt(3));
}
}
public static void main(String[] args) throws Exception {
CallableStatemnetTest cst = new CallableStatemnetTest();
cst.initParam("E:\\JavaCode\\Code\\src\\M13\\mysql.ini");
cst.CallProcedure();
}
}
七、可滚动、可更新结果集
ResultSet除了next()指针外,还可以使用absolute()、previous()、afterLast()等方法自由移动记录指针
在使用Connection创建了Statement或者PreparedStatement是还额外传入如下两个参数
-
resultSetType:控制ResultSet的类型,可以是以下三个值
- ResultSet.TYPE_FORWARD_ONLY:该常量记录指针只能向前移动
- ResultSet.TYPE_SCROLL_INSENSITIVE:该常量控制记录指针可以自动移动(可滚动结果集),但是底层数据的改变不会影响ResultSet的内容变化
- ResultSet.TYPE_SCROLL_SENSITIVE:该常量控制记录指针可以自动移动(可滚动结果集),但是底层数据的改变会影响ResultSet的内容变化
-
ResultSetConcurrent:控制ResultSet的并发类型,该参数可以接收如下两个值
- ResultSet.CONCUR_READ_ONLY:该常量表示ResultSet是只读的并发模式(默认)
- ResultSet.CONCUR_UPDATABLE:该常量表示ResultSet是可更新的并发模式
下面代码通过这两个参数创建一个PreparedStatement对象,有该对象生成的ResultSet对象将是可滚动的、可更新的
//使用Connection创建一个PreparedStatement对象
//传入控制结果集可滚动、可更新的参数
ps = conn.preparedStatement(sql,ResultSet.TYPE_SCROLL_INSENSITIVE,SesultSet.CONCUR_UPDATABLE);
实例代码
/**
* @ClassName: ResultSetTest
* @description: 本段代码主要讨论通过Connection来创建PreparedStatement对象,并通过这一对象来是数据库达到可滚动可更新的目的
* @author: FFIDEAL
* @Date: 2020年4月6日 下午11:18:39
*/
package M13;
import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Properties;
public class ResultSetTest {
private String driver;
private String url;
private String user;
private String pass;
public void initParam(String paramFile) throws Exception{
Properties props = new Properties();
props.load(new FileInputStream(paramFile));
driver = props.getProperty("driver");
url = props.getProperty("url");
user = props.getProperty("user");
pass = props.getProperty("pass");
Class.forName(driver);
}
public void query(String sql) throws Exception{
try(
Connection conn = DriverManager.getConnection(url, user, pass);
PreparedStatement ps = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE);
ResultSet rs = ps.executeQuery();
){
rs.last();
//获得行数
int rowCount = rs.getRow();
System.out.println("共有" + rowCount + "行");
for (int i = rowCount; i > 0; i--) {
//移动指针
rs.absolute(i);
System.out.println(rs.getString(1) + "\t" + rs.getString(2) + "\t" + rs.getString(3));
//修改指针记录所只记录、第2列的值
rs.updateString(2, "学生名" + i);
rs.updateRow();
}
}
}
public static void main(String[] args) throws Exception{
ResultSetTest rst = new ResultSetTest();
rst.initParam("E:\\JavaCode\\Code\\src\\M13\\mysql.ini");
rst.query("select * from student_table");
}
}
八、处理Bob类型的数据
Bob(Binary Long Object)是二进制长对象的意思,Blob列通常用于存储大文件,典型的Blob内容是一张图片或者一个声音文件,由于它的特殊性,必须使用特殊的方法来存储。而Blob列可以吧图片、声音等文件转化为二进制数据保存在数据库里,并可以从数据库中恢复指定文件。
Blob数据插入数据库需要使用PreparedStatement,该对象有一个方法:setBinaryStream()
九、 Java7新增的RowSetFactory与RowSet
RowSet继承自ResultSet接口,RowSet接口下包含JdbcRowSet、CacheRowSet、FilteredRowSet、JoinRowSet和WebRowSet常用子接口。除了JdbcRowSet需要与数据库保持连接以外,其他的子接口都是离线的RowSet,但是并不好用(仅做了解)——可滚动、客休该的特性
Java新增的RowSetFactory接口和RowSetProvider类,其中RowSetProvider负责创建RowSetFactory,而RowSetFactory则提供了以下方法创建RowSet实例
- CachedRowSet createCachedRowSet():创建一个默认的CachedRowSet
- FilteredRowSet createFilteredRowSet():创建一个默认的FilteredRowSet
- JdbcRowSet createJdbcRowSet():创建一个默认的JdbcRowSet
- JoinRowSet createJoinRowSet():创建一个默认的JoinRowSet
- WebRowSet createWebRowSet():创建一个默认的WebRowSet
实例代码
/**
* @ClassName: JdbcRowSetTest
* @description: 本节代码主要讨论通过JdbcRowSetImpl示范了使用JdbcRowSet的可滚动、可修改特性
* @author: FFIDEAL
* @Date: 2020年4月7日 下午4:34:09
*/
package M13;
import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.Properties;
import javax.sql.rowset.JdbcRowSet;
import javax.sql.rowset.RowSetFactory;
import javax.sql.rowset.RowSetProvider;
public class JdbcRowSetTest {
private String driver;
private String url;
private String user;
private String pass;
public void initParam(String paramFile) throws Exception{
Properties props = new Properties();
props.load(new FileInputStream(paramFile));
driver = props.getProperty("driver");
url = props.getProperty("url");
user = props.getProperty("user");
pass = props.getProperty("pass");
Class.forName(driver);
}
public void update(String sql) throws Exception{
//使用RowSetProvider创建RowSetFactory
RowSetFactory rsf = RowSetProvider.newFactory();
try(
//使用RowSetFactory创建默认的JdbcRowSet实例
JdbcRowSet jrs = rsf.createJdbcRowSet()
){
//设置必要的连接信息
jrs.setUrl(url);
jrs.setUsername(user);
jrs.setPassword(pass);
//设置查询语句
jrs.setCommand(sql);
//执行查询
jrs.execute();
jrs.afterLast();
//将前滚动结果集
while(jrs.previous()) {
System.out.println("===1====");
System.out.println(jrs.getString(1)
+ "\t" + jrs.getString(2)
+ "\t" + jrs.getString(3)
+ "\t" + jrs.getInt("student_id"));
if(jrs.getInt("student_id") == 3) {
//修改制定记录行
jrs.updateString("student_name","Java");
System.out.println("===2====");
jrs.updateRow();
}
}
}
}
public static void main(String[] args) throws Exception{
JdbcRowSetTest jrst = new JdbcRowSetTest();
jrst.initParam("E:\\JavaCode\\Code\\src\\M13\\mysql.ini");
jrst.update("select s.* from student_table s");
}
}
十、离线RowSet
使用离线RowSet可以将底层数据库数据读入内存,封装成RowSet对象,该对象不仅安全,编程还比较简单。
实例代码
/**
* @ClassName: CachedRowSetTest
* @description: 本节代码讨论用CachedRowSet来处理读取数据
* @author: FFIDEAL
* @Date: 2020年4月7日 下午6:14:27
*/
package M13;
import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Properties;
import javax.sql.rowset.CachedRowSet;
import javax.sql.rowset.RowSetFactory;
import javax.sql.rowset.RowSetProvider;
public class CachedRowSetTest {
private String driver;
private static String url;
private static String user;
private static String pass;
public void initParam(String paramFile) throws Exception{
Properties props = new Properties();
props.load(new FileInputStream(paramFile));
driver = props.getProperty("driver");
url = props.getProperty("url");
user = props.getProperty("user");
pass = props.getProperty("pass");
Class.forName(driver);
}
public CachedRowSet query(String sql) throws Exception{
Connection conn = DriverManager.getConnection(url, user, pass);
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql);
//使用RowSetProvider创建RowSetFactory
RowSetFactory factory = RowSetProvider.newFactory();
//创建默认的CachedRowSet实例
CachedRowSet cachers = factory.createCachedRowSet();
//使用ResultSet装填RowSet,调用了RowSet的populate(ResultSet rs)方法来包装给定的ResultSet
//接下来就关闭了ResultSet、Statement、Connection等数据库资源
cachers.populate(rs);
//关闭资源
rs.close();
stmt.close();
conn.close();
return cachers;
}
public static void main(String[] args) throws Exception{
CachedRowSetTest ct = new CachedRowSetTest();
ct.initParam("E:\\JavaCode\\Code\\src\\M13\\mysql.ini");
CachedRowSet rs = ct.query("select * from student_table");
rs.afterLast();
//向前滚动结果集
while(rs.previous()) {
System.out.println(rs.getString(1)
+ "\t" + rs.getString(2)
+ "\t" + rs.getString(3));
if(rs.getInt("student_id") == 3) {
//修改制定记录行
rs.updateString("student_name","Java");
rs.updateRow();
}
}
//重新获取数据库连接
Connection conn = DriverManager.getConnection(url, user, pass);
conn.setAutoCommit(false);
//把对RowSet所做的修改同步到底底层数据库
rs.acceptChanges(conn);
}
}
十一、离线RowSet的分页查询
若一次性将数据加载到内存中,就会造成内存资源紧张的问题,因此在CachedRowSet提供了分页的功能
CachedRowSet通过以下方法控制分页
- populate(ResultSet rs, int satrtRow):使用给定的ResultSet装填RowSet,从ResultSet的第startRow条记录开始填装
- setPageSize(int PageSize):设置CachedRowSet每次返回多少条记录
- previousPage():在底层ResultSet可用的情况下,让CachedRowSet读取上一页记录
- nextPage():在底层ResultSet可用的情况下,让CachedRowSet读取下一页记录
实例代码
/**
* @ClassName: CachedRowSetPage
* @description: 本节代码主要讨论RowSet的查询分页的问题
* @author: FFIDEAL
* @Date: 2020年4月7日 下午8:02:59
*/
package M13;
import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Properties;
import javax.sql.rowset.CachedRowSet;
import javax.sql.rowset.RowSetFactory;
import javax.sql.rowset.RowSetProvider;
public class CachedRowSetPage {
private String driver;
private static String url;
private static String user;
private static String pass;
public void initParam(String paramFile) throws Exception{
Properties props = new Properties();
props.load(new FileInputStream(paramFile));
driver = props.getProperty("driver");
url = props.getProperty("url");
user = props.getProperty("user");
pass = props.getProperty("pass");
Class.forName(driver);
}
public CachedRowSet query(String sql, int pageSize, int page) throws Exception{
try(
Connection conn = DriverManager.getConnection(url, user, pass);
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql);){
//要通过RowSetprovider创建RowSetFactory
RowSetFactory factory = RowSetProvider.newFactory();
//通过RowSetFactory创建默认的CachedRowSet
CachedRowSet cachedRs = factory.createCachedRowSet();
//显示每页显示的pagesize条记录
cachedRs.setPageSize(10);
//使用ResultSet装填RowSet,设置从第几条记录开始
cachedRs.populate(rs, (page - 1) * pageSize + 1);
return cachedRs;
}
}
public static void main(String[] args) throws Exception{
CachedRowSetPage cachedRsp = new CachedRowSetPage();
cachedRsp.initParam("E:\\JavaCode\\Code\\src\\M13\\mysql.ini");
//显示要看第十页的记录,每页显示10行
CachedRowSet rs = cachedRsp.query("select * from student_table", 10, 10);
//向后滚动
while(rs.next()) {
System.out.println(rs.getString(1)
+ "\t" + rs.getString(2)
+ "\t" + rs.getString(3));
}
}
}
十二、事务处理
事务具备四个特性(ACID):分别为原子性、一致性、隔离性、持续性
数据库事务由以下语句组成:
- 一组DML,经过这组DML语句修改之后数据保持较好的一致性
- 一条DDL语句
- 一条DCL语句
提交事务:显示提交(commit);自动提交(执行DDL或者DCL语句,或者程序正常退出)
事务遇到执行失败会如何——回滚:显式回滚(rollback);自动回滚(系统错误或者强行退出)
#历史开始事务
begin
#向student_table表中插入3条数据
insert into jdbc_test
values(null,'xx',1);
insert into jdbc_test
values(null,'yy',1);
insert into jdbc_test
values(null,'zz',1);
#查询student_table表的记录
select * from jdbc_test;
#回滚事务①
rollback;
#再次查询②
select * from jdbc_test;
以上代码在①处发现插入的三条记录看不到了(隔离性),执行②时看到的是刚插入时候的样子
在JDBC中可以调用Connection的setAutoCommit()方法来关闭自动提交,开启事务;同时可以通过Connection中 getAutoCommit()方法来返回该链接的自动提交模式
//关闭自动提交,开启事务
conn.setAutoCommit(false);
一旦事务创建完毕,就像平时创建Statement对象,然后执行任意多条语句
stmt.executeUpdate(...);
stmt.executeUpdate(...);
stmt.executeUpdate(...);
以上代码只是被执行但是没有生效,最后要调用Connection中的commit()来提交
conn.commit(); //提交事务
如果任意一条语句失败,就要调用Connection中的rollback()来回滚
//回滚事务
conn.rollback();
实例代码
/**
* @ClassName: TranscationTest
* @description: 本节代码讨论了当程序出现了SQLException错误时,系统将回滚事务
* @author: FFIDEAL
* @Date: 2020年4月7日 下午10:17:59
*/
package M13;
import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
import java.util.Properties;
public class TranscationTest {
private String driver;
private static String url;
private static String user;
private static String pass;
public void initParam(String paramFile) throws Exception{
Properties props = new Properties();
props.load(new FileInputStream(paramFile));
driver = props.getProperty("driver");
url = props.getProperty("url");
user = props.getProperty("user");
pass = props.getProperty("pass");
Class.forName(driver);
}
public void insetInTransaction(String[] sqls) throws Exception{
try(
Connection conn = DriverManager.getConnection(url, user, pass);
){
//关闭自动提交,开启事务
conn.setAutoCommit(false);
try(
//使用Connection来创建一个Statement对象
Statement stmt = conn.createStatement();
){
//循环多次执行SQL语句
for(String sql:sqls) {
stmt.executeUpdate(sql);
}
}
conn.commit();
}
}
public static void main(String[] args) throws Exception{
TranscationTest tt = new TranscationTest();
tt.initParam("E:\\JavaCode\\Code\\src\\M13\\mysql.ini");
String[] sqls = new String[] {
"insert into jdbc_test values(null,'xx',1);",
"insert into jdbc_test values(null,'yy',1);",
"insert into jdbc_test values(null,'zz',1);",
"insert into jdbc_test values(null,'ccc',5)"
};
tt.insetInTransaction(sqls);
System.out.println("==");
}
}
十三、使用DatabaseMetaData分析数据库信息
JDBC提供了DatabaseMetaData来封装数据库信息,通过Connection提供的getMetaData()就可以获取数据库对应的DatabaseMetaData对象
实例代码
/**
* @ClassName: DatabaseMetaDataTest
* @description: 本节代码主要讨论通过DatabaseMetaData分析当前Connection连接对应数据库的一些基本信息,
* 包括当前数据库包含多少数据表,存储过程,student_table表的数据列、主键、外键等信息
* @author: FFIDEAL
* @Date: 2020年4月7日 下午11:05:41
*/
package M13;
import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;
import com.mysql.cj.jdbc.result.ResultSetMetaData;
public class DatabaseMetaDataTest {
private String driver;
private static String url;
private static String user;
private static String pass;
public void initParam(String paramFile) throws Exception{
Properties props = new Properties();
props.load(new FileInputStream(paramFile));
driver = props.getProperty("driver");
url = props.getProperty("url");
user = props.getProperty("user");
pass = props.getProperty("pass");
Class.forName(driver);
}
public void info() throws Exception{
try(
Connection conn = DriverManager.getConnection(url, user, pass);){
//获取DatabaseMetaData对象
DatabaseMetaData dbmd = conn.getMetaData();
//获取MySQL支持的所有表类型
ResultSet rs = dbmd.getTableTypes();
System.out.println("===mysql支持表类型信息===");
printResultSet(rs);
//获取当前数据库中全部数据表
rs = dbmd.getTables(null, null, "%", new String[] {"TABLE"});
System.out.println("===当前数据库里的数据表信息===");
printResultSet(rs);
//获取student_table表中的主键
rs = dbmd.getPrimaryKeys(null, null, "student_table");
System.out.println("===student_table表中主键信息===");
printResultSet(rs);
//获取当前数据库全部的存储过程
rs = dbmd.getProcedures(null, null, "%");
System.out.println("===当前数据库中所有的存储过程===");
printResultSet(rs);
//获得teacher_table和student_table表之间的外键约束
rs = dbmd.getCrossReference(null, null, "student_table", null, null, "teacher_table");
System.out.println("===获得teacher_table和student_table表之间的外键约束===");
printResultSet(rs);
//获得student_table表的全部数据列
rs = dbmd.getColumns(null, null, "student_table", "%");
System.out.println("获得student_table表的全部数据列");
printResultSet(rs);
}
}
/**
* @param rs
*/
private void printResultSet(ResultSet rs) throws SQLException{
// TODO Auto-generated method stub
java.sql.ResultSetMetaData rsmd = rs.getMetaData();
//打印ResultSet所有的列标题
for (int i = 0; i < rsmd.getColumnCount(); i++) {
System.out.print(rsmd.getColumnName(i + 1) + "\t");
}
System.out.println("");
//打印ResultSet所有的数据
while(rs.next()) {
for (int i = 0; i < rsmd.getColumnCount(); i++) {
System.out.print(rs.getString(i + 1) + "\t");
}
System.out.println("");
}
rs.close();
}
public static void main(String[] args) throws Exception{
DatabaseMetaDataTest dmdt = new DatabaseMetaDataTest();
dmdt.initParam("E:\\JavaCode\\Code\\src\\M13\\mysql.ini");
dmdt.info();
}
}
十四、使用连接池管理连接
数据库连接池是Connection对象工厂。数据库连接池的主要参数有:
- 数据库的初始连接数
- 连接池的最大连接数
- 连接池的最小连接数
- 连接池每次增加的容量
实例代码1
//以下代码示范了Tomcat使用DBCP获取数据库的方式
//创建数据源对象
BasicDataSource ds = new BasicDataSource();
//设置连接池所需的驱动
ds.setDriverClassName("com.mysql.jc.jdbc.Driver");
//设置连接数据库的URL
ds.setUrl("jdbc:mysql://127.0.0.1:3306/crazyjavajdbc?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC");
//设置连接池数据库的用户名
ds.setUsername("root");
//设置连接数据库的密码
ds.setPassword("root");
//设置连接池的初始连接数
ds.setInitialSize(5);
//设置连接池最多可有多少个活动连接数
ds.setMaxActive(20);
//设置连接池中最小有2个空闲的连接
ds.setMinIdle(2);
//所依赖的jar包:commons-dbcp.jar/commons-pool.jar
//通过数据源获取数据库连接诶
Connection conn = ds.getConnection();
//释放数据库连接
conn.close();
实例代码2
//以下代码示范了C3P0连接数据库的方式
//创建数据源对象
ComboPooledDataSource ds = new ComboPooledDataSource();
//设置连接池所需的驱动
ds.setDriverClass("com.mysql.jc.jdbc.Driver");
//设置连接数据库的URL
ds.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/crazyjavajdbc?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC");
//设置连接池数据库的用户名
ds.setUser("root");
//设置连接数据库的密码
ds.setPassword("root");
//设置连接池的初始连接数
ds.setInitialPoolSize(5);
//设置连接池最多可有多少个活动连接数
ds.setMaxPoolSize(20);
//设置连接池中最小有2个空闲的连接
ds.setMinPoolSize(2);
//所依赖的jar包:c3p0-0.9.1.2.jar
//通过数据源获取数据库连接诶
Connection conn = ds.getConnection();
//释放数据库连接
conn.close();