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语句的方法: 1 execute 可执行任何SQL语句。- 返回一个boolean值, 如果执行后第一个结果是ResultSet,则返回true,否则返回false 2 executeQuery 执行Select语句 - 返回查询到的结果集 3 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) + " " + rs.getString(2) + " " + rs.getString(3) + " " + 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.util.*; 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) + " "); } System.out.print(" "); } } } 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) + " " + rs.getString(2) + " " + 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) + " "); } System.out.print(" "); // 打印ResultSet里的全部数据 while (rs.next()) { for (int i = 0; i < rsmd.getColumnCount() ; i++ ) { System.out.print(rs.getString(i + 1) + " "); } System.out.print(" "); } rs.close(); } public static void main(String[] args) throws Exception { DatabaseMetaDataTest dt = new DatabaseMetaDataTest(); dt.initParam("mysql.ini"); dt.info(); } }