【Java数据库】使用JDBC操作MySQL数据库、Batch批处理 、事务的概念

MySQL 数据库的命令行操作

登陆操作mysql -hlocalhost –uroot –p123456
退出操作exit
数据库操作建库:create database 库名;
卸载库:drop database 库名;
显示所有数据库:show databases;
选择库:use testjdbc;

表操作建表的sql语句
显示库中所有表:show tables;
显示某个表的结构:describe testjdbc;
SQL操作select语句;insert语句; update语句; delete语句;
表操作DDL语句(create, alter, drop等);


Java使用JDBC

先下载一个mysql-connector-java-8.0.16.jar,其他版本也行
在这里插入图片描述
把这个jar添加到Java Build Path中(我为了避免以后移动jar的时候找不到,就直接复制到项目路径下了)
在这里插入图片描述
【Java数据库】使用JDBC操作MySQL数据库、Batch批处理 、事务的概念_第1张图片


JDBC常用接口

(1)Driver接口

– Driver接口由数据库厂家提供,对于java开发者而言,只需要使用
Driver接口就可以了。
– 在编程中要连接数据库,必须先装载特定厂商的数据库驱动程序。不
同的数据库有不同的装载方法。
– 驱动:就是各个数据库厂商实现的Sun公司提出的JDBC接口。 即对
Connection等接口的实现类的jar文件
– 装载MySql驱动
Class.forName("com.mysql.jdbc.Driver");
– 装载Oracle驱动
Class.forName("oracle.jdbc.driver.OracleDriver");

(2)DriverManager接口
– DriverManager是JDBC的管理层,作用于用户和驱动程序之间。
– DriverManager跟踪可用的驱动程序,并在数据库和相应的驱动程序
之间建立连接。

(3)Connection接口
– Connection与特定数据库的连接(会话),在连接上下文中执行 SQL
语句并返回结果。
– DriverManager的getConnection()方法建立在JDBC URL中定义的数
据库Connection连接上
– 连接MYSQL数据库:

Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc?serverTimezone=UTC", "root", "123456");// sql包中的接口

– 连接ORACLE数据库:

Connection con = DriverManager.getConnection("jdbc:oracle:thin:@host:port:databse","user","password");

注意:建立connection是比较耗时、耗资源的,(连接对象内部其实包含的Socket对象,是一个远程的连接,比较耗时!这是Connection对象管理的一个要点!)经过测试,在本机上建立链接,耗时235ms。在真正的开发中,为了提高效率,都会使用连接池来管理连接对象!

(4)Statement接口:用于执行SQL语句、返回结果,是查询操作的核心
– 用于执行静态 SQL 语句并返回它所生成结果的对象。

– 三种Statement类:
• Statement:
– 由createStatement创建,用于发送简单的SQL语句。(不带参数的)
• PreparedStatement:
– 继承自Statement接口,由prepareStatement创建,用于发送含有一个或多个输入参数的sql语句。PreparedStatement对象比Statement对象的效率更高,并且可以防止SQL注入。我们一般都用PreparedStatement.
• CallableStatement:
– 继承自PreparedStatement 。由方法prePareCall创建,用于调用存储过程。

– 常用的Statement方法:
execute():运行语句,返回是否有结果集。
executeQuery():运行select语句,返回ResultSet结果集。
executeUpdate():运行insert/update/delete操作,返回更新的行数。


Java中连接数据库的使用示例

// 测试MySQL的连接
package cn.hanquan.jdbcTest;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class JDBCTest {
	public static void main(String[] args) {
		try {
			// 加载驱动类
			Class.forName("com.mysql.cj.jdbc.Driver");
			// 建立链接
			Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc?serverTimezone=UTC", "root", "123456");// sql包中的接口
			System.out.println(conn);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
}
  • 常见问题(1)

The server time zone value ‘?й???’ is unrecognized or represents more than one time zone.
【Java数据库】使用JDBC操作MySQL数据库、Batch批处理 、事务的概念_第2张图片

  • 解决方式

使用的数据库是MySQL,驱动是8.0.16,这是由于数据库和系统时区差异所造成的。

在jdbc连接的url后面加上serverTimezone=GMT即可解决问题:

Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc?serverTimezone=UTC", "root", "123456");// sql包中的接口

如果需要使用gmt+8时区,需要写成GMT%2B8,否则会被解析为空。

再一个解决办法就是:使用低版本的MySQL jdbc驱动,5.1.28不会存在时区的问题。


  • 常见问题(2)

Loading class ‘com.mysql.jdbc.Driver’. This is deprecated. The new driver class is ‘com.mysql.cj.jdbc.Driver’. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.

  • 解决方式
    Class.forName("com.mysql.jdbc.Driver");替换成Class.forName("com.mysql.cj.jdbc.Driver");即可

执行SQL语句示例

  • 向表中插入一条数据
package cn.hanquan.jdbcTest;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;

public class JDBCTest {
	public static void main(String[] args) {
		try {
			// 加载驱动类
			Class.forName("com.mysql.cj.jdbc.Driver");
			// 建立链接
			Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc?serverTimezone=UTC",
					"root", "123456");
			// 执行语句
			Statement stat = conn.createStatement();
			String sql = "INSERT INTO user (id, pwd) VALUES ('testName', '666666');";
			stat.execute(sql);

		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
}

实际开发中,使用Statement比较少,因为参数需要拼接字符串来做。处理参数不方便。
另外,使用Statement容易发生SQL注入的危险。

为了避免注入,我们使用PreparedStatement

execute():运行语句,返回是否有结果集。
executeQuery():运行select语句,返回ResultSet结果集。
executeUpdate():运行insert/update/delete操作,返回更新的行数。

package cn.hanquan.jdbcTest;

import java.sql.Connection;
import java.sql.Date;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class JDBCTest {
	public static void main(String[] args) {
		try {
			// 加载驱动类
			Class.forName("com.mysql.cj.jdbc.Driver");

			// 建立链接
			Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc?serverTimezone=UTC",
					"root", "123456");

			// 执行语句
			String sql = "INSERT INTO user (id, pwd,date) VALUES (?,?,?);";// ?占位符 避免拼接字符串时SQL注入
			PreparedStatement ps = conn.prepareStatement(sql);
			ps.setString(1, "高淇");// 可以设置数据类型 参数索引从1开始计算
			ps.setString(2, "888888");
			ps.setDate(3, new Date(System.currentTimeMillis()));// import java.sql.Date;
			ps.execute();// 返回是否有结果集

			ps.setObject(1, "老高");// 不关心数据类型
			ps.setObject(2, "222222");
			ps.setObject(3, new Date(System.currentTimeMillis()));
			int count = ps.executeUpdate();// 返回影响的数据条数
			System.out.println(count);

		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
}

在这里插入图片描述

  • 查找数据,返回结果集

关于rs.next()的说明
【Java数据库】使用JDBC操作MySQL数据库、Batch批处理 、事务的概念_第3张图片
代码

package cn.hanquan.jdbcTest;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class JDBCTest {
	public static void main(String[] args) {
		Connection conn = null;
		PreparedStatement ps = null;
		try {
			// 加载驱动类
			Class.forName("com.mysql.cj.jdbc.Driver");

			// 建立链接
			conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc?serverTimezone=UTC", "root",
					"123456");

			// 执行语句
			String sql = "select * from user";
			ps = conn.prepareStatement(sql);
			ResultSet rs = ps.executeQuery();// 返回结果集
			while (rs.next()) {// 游标 返回true
				System.out.print(rs.getObject(1) + " ");// 取出第1列
				System.out.print(rs.getObject(2) + " ");// 取出第2列
				System.out.print(rs.getObject(3) + " ");// 取出第3列
				System.out.println(rs.getObject(4) + " ");// 取出第4列
			}

		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {// 关闭顺序:resultset->statement->connection 三个try catch一定要分开写 否则一个异常后面都不执行了
			try {
				if (ps != null) {
					ps.close();
				}
			} catch (SQLException e) {
				e.printStackTrace();
			}
			try {
				if (conn != null) {
					conn.close();
				}
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}
}

输出

老高 222222 110 2019-08-11 
高淇 888888 119 2019-08-11 

批处理 Batch:20000条数据大约2秒

对于大量的批处理,建议使用Statement,因为PreparedStatement的预编译空间有限,当数据量特别大时,会发生异常。

package cn.hanquan.jdbcTest;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;

public class JDBCTest {
	public static void main(String[] args) {
		Connection conn = null;
		Statement stat = null;
		try {
			// 加载驱动类
			Class.forName("com.mysql.cj.jdbc.Driver");
			conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc?serverTimezone=UTC", "root",
					"123456");
			conn.setAutoCommit(false);// 把事务设置为手动提交

			stat = conn.createStatement();
			for (int i = 0; i < 100; i++) {
				stat.addBatch(
						"INSERT INTO user (id, pwd, phone, date) VALUES ('批量" + i + "', 'password', 100000, now());");
			}
			stat.executeBatch();
			conn.commit();// 提交事务

		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			try {
				if (stat != null) {
					stat.close();
				}
			} catch (SQLException e) {
				e.printStackTrace();
			}
			try {
				if (conn != null) {
					conn.close();
				}
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}
}

【Java数据库】使用JDBC操作MySQL数据库、Batch批处理 、事务的概念_第4张图片


事务的概念

一组事务要么同时执行成功,要么同时执行失败的SQL语句。是数据库操作的一个执行单元!

– 事务开始于:
• 连接到数据库上,并执行一条DML语句(INSERT、UPDATE或DELETE)。
• 前一个事务结束后,又输入了另外一条DML语句。

– 事务结束于:
• 执行COMMIT或ROLLBACK语句。
• 执行一条DDL语句,例如CREATE TABLE语句;在这种情况下,会自动执行COMMIT语句。
• 执行一条DCL语句,例如GRANT语句;在这种情况下,会自动执行COMMIT语句。
• 断开与数据库的连接。
• 执行了一条DML语句,该语句却失败了;在这种情况中,会为这个无效的DML语句执行ROLLBACK语句。

事务的四大特点(ACID)

– atomicity(原子性)
• 表示一个事务内的所有操作是一个整体,要 么全部成功,要么全失败;

– consistency(一致性)
• 表示一个事务内有一个操作失败时,所有的更改过的数据都必须回滚到修改前的状态;

– isolation(隔离性)
• 事务查看数据时数据所处的状态,要么是另一并发事务修改它之前的状态,要么是另一事务修改它之后的状态,事务不会查看中间状态的数据。

– durability(持久性)
• 持久性事务完成之后,它对于系统的影响是永久性的。

事务隔离级别从低到高:
– 读取未提交(Read Uncommitted)
– 读取已提交(Read Committed)
– 可重复读(Repeatable Read)
– 序列化(serializable)

代码示例:使用事务,如果某一条语句执行失败,自动回滚conn.rollback()

package cn.hanquan.jdbcTest;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class JDBCTest {
	public static void main(String[] args) throws SQLException {
		Connection conn = null;
		PreparedStatement ps1 = null;
		try {
			// 加载驱动类
			Class.forName("com.mysql.cj.jdbc.Driver");
			conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc?serverTimezone=UTC", "root",
					"123456");
			conn.setAutoCommit(false);// 将事务设置为手动提交

			for (int i = 0; i < 10; i++) {
				ps1 = conn.prepareStatement("INSERT INTO user (id, pwd, phone, date) VALUES (?, ?, ?, now());");
				ps1.setObject(1, "auto" + i);
				ps1.setObject(2, "000000" + i);
				ps1.setObject(3, "10000" + i);
				ps1.execute();
			}
			
			conn.commit();// 提交事务

		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (SQLException e) {
			e.printStackTrace();
			conn.rollback();// 执行失败 回滚 我测试了一下 如果不加这句话 也会自动回滚 不知道为啥
		} finally {
			try {
				if (ps1 != null) {
					ps1.close();
				}
			} catch (SQLException e) {
				e.printStackTrace();
			}
			try {
				if (conn != null) {
					conn.close();
				}
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}
}

你可能感兴趣的:(Java,MySQL)