1.先用上代码
private static void jdbcTest() {
// 准备链接对象
Connection connection = null;
// 准备预编译的sql语句对象
PreparedStatement preparedStatement = null;
// 准备结果接收对象
ResultSet resultSet = null;
try {
//加载驱动类
Class.forName("com.mysql.jdbc.Driver");
//通过java规定的DriverManager获取链接对象
connection = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/world?characterEncoding=utf8&useSSL=true", "root", "123456");
//准备sql语句到预编译对象
String sql = "select * from city where name=?";
// 构建预编译对象
preparedStatement = connection.prepareStatement(sql);
// 设置预编译对象参数
preparedStatement.setString(1, "Kabul");
// 执行预编译对象得到结果对象(用于查询)
resultSet = preparedStatement.executeQuery();
// 执行预编译对象得到结果对象(用于DDL或者DML语句)
//preparedStatement.executeUpdate();
// 循环获取结果值
while (resultSet.next()) {
System.out.println(resultSet.getString("name") + " " + resultSet.getString("ID"));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//资源释放
if (resultSet != null) {
try {
resultSet.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if (preparedStatement != null) {
try {
preparedStatement.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
SQL语句主要分为五种:
1. 查询语句:主要由select关键字完成,查询语句是SQL语句中最复杂、功能最丰富的语句;
2. DML(Data Manipulation Language,数据操作语言):包含insert、update、delete
3. DDL(Data Definition Language, 数据定义语言):包含create、 alter、drop、truncate
4. DCL(Data Control Language,数据控制语言):包含grant 、revoke
5. 事务控制语句: commit、rollback、savepoint
JDBC编程步骤:
1. 加载数据库驱动。通过Class类的forName()静态方法来加载驱动
Class.forName(driverClass)
2. 通过DriverManager获取数据库连接Connection对象。
DriverManager.getConnection(String url,String user,String pass)
3. 通过Connection对象创建Statement对象。创建Statement有三种方法。
- createStatement(): 创建基本的Statement对象
- prepareStatement(String sql): 根据传入的SQL语句创建预编译的Statement对象
- prepareCall(String sql): 根据传入的SQL语句创建CallableStatement对象。
4. 使用Statement执行SQL语句。
execute(): 可以执行任何SQL语句,但比较麻烦,在不清SQL语句类型时,只能使用这个方法。
executeUpdate():主要用于执行DML和DDL语句。执行DML语句返回受SQL语句影响的行数,执行DDL语句返回0。
executeQuery():只能执行查询语句,执行后返回代表查询结果的ResultSet对象。
5. 操作结果集。有两类方法来操作结果集:
- next()、 previous()、 first()、 last()、 beforeFirst()、 afterLast()、 absolute()
- getXxx()方法获取记录指针指向行、特定列的值。
6. 回收数据库资源,包括ResultSet、Statement和Connection等资源。
import java.sql.*;
public class ConnMySql
{
public static void main(String[] args) throws Exception
{
// 1.加载驱动,使用反射的知识,现在记住这么写。
Class.forName("com.mysql.jdbc.Driver");
try(
// 2.使用DriverManager获取数据库连接,
// 其中返回的Connection就代表了Java程序和数据库的连接
// 不同数据库的URL写法需要查驱动文档知道,用户名、密码由DBA分配
Connection conn = DriverManager.getConnection(
"jdbc:mysql://127.0.0.1:3306/select_test"
, "root" , "32147");
// 3.使用Connection来创建一个Statment对象
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 t.teacher_id = s.java_teacher"))//在try语句中,执行完后会自动关闭。
{
// 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));
}
}
}
}
执行DDL语句的例子:
import java.util.*;
import java.io.*;
import java.sql.*;
public class ExecuteDDL
{
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来创建一个Statment对象
Statement stmt = conn.createStatement())
{
// 执行DDL,创建数据表
stmt.executeUpdate(sql);
}
}
public static void main(String[] args) throws Exception
{
ExecuteDDL ed = new ExecuteDDL();
ed.initParam("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("-----建表成功-----");
}
}
执行DML语句的例子 :
import java.util.*;
import java.io.*;
import java.sql.*;
public class ExecuteDML
{
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 int insertData(String sql)throws Exception//注意这里的返回值类型
{
// 加载驱动
Class.forName(driver);
try(
// 获取数据库连接
Connection conn = DriverManager.getConnection(url
, user , pass);
// 使用Connection来创建一个Statment对象
Statement stmt = conn.createStatement())
{
// 执行DML,返回受影响的记录条数
return stmt.executeUpdate(sql);
}
}
public static void main(String[] args)throws Exception
{
ExecuteDML ed = new ExecuteDML();
ed.initParam("mysql.ini");
int result = ed.insertData("insert into jdbc_test(jdbc_name,jdbc_desc)"
+ "select s.student_name , t.teacher_name "
+ "from student_table s , teacher_table t "
+ "where s.java_teacher = t.teacher_id;");
System.out.println("--系统中共有" + result + "条记录受影响--");
}
}
当不清楚SQL语句类型时的例子:
import java.io.*;
import java.sql.*;
public class ExecuteSQL
{
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 executeSql(String sql)throws Exception
{
// 加载驱动
Class.forName(driver);
try(
// 获取数据库连接
Connection conn = DriverManager.getConnection(url
, user , pass);
// 使用Connection来创建一个Statement对象
Statement stmt = conn.createStatement())
{
// 执行SQL,返回boolean值表示是否包含ResultSet
boolean hasResultSet = stmt.execute(sql);
// 如果执行后有ResultSet结果集
if (hasResultSet)
{
try(
// 获取结果集
ResultSet rs = stmt.getResultSet())
{
// ResultSetMetaData是用于分析结果集的元数据接口
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.print("\n");
}
}
}
else
{
System.out.println("该SQL语句影响的记录有"
+ stmt.getUpdateCount() + "条");
}
}
}
public static void main(String[] args) throws Exception
{
ExecuteSQL es = new ExecuteSQL();
es.initParam("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");
System.out.println("------执行查询数据的查询语句-----");
es.executeSql("select * from my_test");
}
}
使用PreparedStatement执行SQL语句的例子(使用这种方法,可以防止SQL注入,比如在登录框中输入‘or true or’)
import java.util.*;
import java.io.*;
import java.sql.*;
public class PreparedStatementTest
{
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");
// 加载驱动
Class.forName(driver);
}
public void insertUseStatement()throws Exception
{
long start = System.currentTimeMillis();
try(
// 获取数据库连接
Connection conn = DriverManager.getConnection(url
, user , pass);
// 使用Connection来创建一个Statment对象
Statement stmt = conn.createStatement())
{
// 需要使用100条SQL语句来插入100条记录
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 insertUsePrepare()throws Exception
{
long start = System.currentTimeMillis();
try(
// 获取数据库连接
Connection conn = DriverManager.getConnection(url
, user , pass);
// 使用Connection来创建一个PreparedStatement对象
PreparedStatement pstmt = conn.prepareStatement(
"insert into student_table values(null,?,1)"))
{
// 100次为PreparedStatement的参数设值,就可以插入100条记录
for (int i = 0; i < 100 ; i++ )
{
pstmt.setString(1 , "姓名" + i);
pstmt.executeUpdate();
}
System.out.println("使用PreparedStatement费时:"
+ (System.currentTimeMillis() - start));
}
}
public static void main(String[] args) throws Exception
{
PreparedStatementTest pt = new PreparedStatementTest();
pt.initParam("mysql.ini");
pt.insertUseStatement();
pt.insertUsePrepare();
}
}
使用CallableStatement调用存储过程的例子
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.*;
import java.io.*;
import java.sql.*;
public class CallableStatementTest
{
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 callProcedure()throws Exception
{
// 加载驱动
Class.forName(driver);
try(
// 获取数据库连接
Connection conn = DriverManager.getConnection(url
, user , pass);
// 使用Connection来创建一个CallableStatment对象
CallableStatement cstmt = conn.prepareCall(
"{call add_pro(?,?,?)}"))
{
cstmt.setInt(1, 4);
cstmt.setInt(2, 5);
// 注册CallableStatement的第三个参数是int类型
cstmt.registerOutParameter(3, Types.INTEGER);
// 执行存储过程
cstmt.execute();
// 获取,并输出存储过程传出参数的值。
System.out.println("执行结果是: " + cstmt.getInt(3));
}
}
public static void main(String[] args) throws Exception
{
CallableStatementTest ct = new CallableStatementTest();
ct.initParam("mysql.ini");
ct.callProcedure();
}
}
管理结果集:
可滚动、可更新的结果集
需要在创建Statement或者PrepareStatement时传入额外的参数,如下面例子:
import java.util.*;
import java.io.*;
import java.sql.*;
public class ResultSetTest
{
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 query(String sql)throws Exception
{
// 加载驱动
Class.forName(driver);
try(
// 获取数据库连接
Connection conn = DriverManager.getConnection(url , user , pass);
// 使用Connection来创建一个PreparedStatement对象
// 传入控制结果集可滚动,可更新的参数。
PreparedStatement pstmt = conn.prepareStatement(sql
, ResultSet.TYPE_SCROLL_INSENSITIVE
, ResultSet.CONCUR_UPDATABLE);
ResultSet rs = pstmt.executeQuery())
{
rs.last();
int rowCount = rs.getRow();
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 rt = new ResultSetTest();
rt.initParam("mysql.ini");
rt.query("select * from student_table");
}
}
处理Blob类型数据
Blob(Binary Long object)是二进制长对象的意思。使用该类可以吧图片、声音等文件的二进制数据保存在数据库中,并可以从数据库中恢复指定文件。
通过PrepareStatement的setBinaryStream(int parameterIndex,InputStream x),该方法可以指定参数传入二进制流。
如果需要从ResultSet中取出Blob数据,可以调用ResultSet的getBlob(int columnIndex)来返回一个Blob对象,然后再通过Blob对象的getBinaryStream()方法获取该Blob数据的输入流,也可以使用Blob对象的getBytes()方法直接读取该Blob对象封装的二进制数据。
使用ResultSetMetaData分析结果集:
该对象可以通过ResultSet的getMetaData()方法来放回。常用的方法有:
- int getColumnCount():返回该ResultSet的列数量
- String getColumnName(int column):返回指定索引的列名
- int getColumnType(int column):返回指定索引的列类型
事务处理
事务是由一步或几步数据库操作序列组成的逻辑执行单元,这系列操作要么全部执行,要么全部放弃执行。
事务有四个特性:原子性、一致性、隔离性、持续性。
数据库事务由下列语句组成。
一组DML语句,一条DDL语句,一条DCL语句。DDL和DCL语句最多只能有一条,因为DDL和DCL语句都会导致事务立即提交。
事务提交方式(没有提交前事务中的语句不会生效)
1.显式提交:使用commit
2.自动提交:执行DDL或DCL语句,或者程序正常退出。
事务回滚方式:(没有生效的事务可以回滚)
1.显式回滚:使用rollback
2.自动回滚:系统错误或者强行退出
自动提交和开始事务是对立的,开始事务功能后,就会将自动提交的功能关闭。正常情况下需要手动关闭自动提交。
JDBC对事务的支持
可以调用Connection的setAutoCommit()方法来关闭自动提交,开启事务。getAutoCommit()返回自动提交模式。commit()提交事务。rollback()回滚事务。
例子:
import java.sql.*;
import java.io.*;
import java.util.*;
public class TransactionTest
{
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 insertInTransaction(String[] sqls) throws Exception
{
// 加载驱动
Class.forName(driver);
try(
Connection conn = DriverManager.getConnection(url , user , pass))
{
// 关闭自动提交,开启事务
conn.setAutoCommit(false);
try(
// 使用Connection来创建一个Statment对象
Statement stmt = conn.createStatement())
{
// 循环多次执行SQL语句
for (String sql : sqls)
{
stmt.executeUpdate(sql);
}
}
// 提交事务
conn.commit();
}
}
public static void main(String[] args) throws Exception
{
TransactionTest tt = new TransactionTest();
tt.initParam("mysql.ini");
String[] sqls = new String[]{
"insert into student_table values(null , 'aaa' ,1)",
"insert into student_table values(null , 'bbb' ,1)",
"insert into student_table values(null , 'ccc' ,1)",
// 下面这条SQL语句将会违反外键约束,
// 因为teacher_table中没有ID为5的记录。
"insert into student_table values(null , 'ccc' ,5)" //①
};
tt.insertInTransaction(sqls);
}
}
使用DatabaseMetaData分析数据库信息
通过Connection提供的getMetaData()方法获取DatabaseMetaData对象。许多DatabaseMetaData方法是以ResultSet对象的形式返回查询信息,然后使用ResultSet中的常规方法获取内容。如果没有内容就返回一个空的ResultSet对象。
DatabaseMetaData的许多方法都需要传入一个xxxPattern模式字符串,这里的xxxPattern不是正则表达式,而是SQL里的模式字符串。
即用百分号(%)代表任意多个字符,使用下划线(_)代表一个字符。通常情况下,如果把该模式字符串的参数值设置为null,即表明该参数不过为过滤条件。
例子:
import java.sql.*;
import java.util.*;
import java.io.*;
public class DatabaseMetaDataTest
{
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 info() throws Exception
{
// 加载驱动
Class.forName(driver);
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, "teacher_table"
, null, null, "student_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);
}
}
public void printResultSet(ResultSet rs)throws SQLException
{
ResultSetMetaData rsmd = rs.getMetaData();
// 打印ResultSet的所有列标题
for (int i = 0 ; i < rsmd.getColumnCount() ; i++ )
{
System.out.print(rsmd.getColumnName(i + 1) + "\t");
}
System.out.print("\n");
// 打印ResultSet里的全部数据
while (rs.next())
{
for (int i = 0; i < rsmd.getColumnCount() ; i++ )
{
System.out.print(rs.getString(i + 1) + "\t");
}
System.out.print("\n");
}
rs.close();
}
public static void main(String[] args)
throws Exception
{
DatabaseMetaDataTest dt = new DatabaseMetaDataTest();
dt.initParam("mysql.ini");
dt.info();
}
}