MySQL数据库学习--JDBC

MySQL数据库学习--JDBC

  • JDBC概述
  • JDBC完成增、删、改、查
  • 结果集处理
    • 得到结果集
    • 结果集光标
      • 移动行光标到固定位置
      • 确认行光标位置
      • 上下移动光标
    • 结果集元数据
  • PreparedStatement
    • PreparedStatement的使用
    • 预处理
  • JdbcUtils 1.0 获取数据库连接
  • 时间类型转换
  • 批处理
  • 关键字

JDBC概述

JDBC(Java DataBase Connectivity)就是Java数据库连接。即使用Java语言操作数据库
由SUN公司提供的一套访问数据库的规范,并提供连接数据库的协议标准。各个数据库厂商遵循SUN的规范提供一套访问自己公司数据库服务器的API。
MySQL数据库学习--JDBC_第1张图片
操作步骤:

  • 导jar包:驱动
  • 加载驱动类(MySQL:com.mysql.jdbc.Driver)
  • 给url、username、password 用于连接数据库
  • 使用DriverManager类连接数据库
/** 
 * 加载驱动类,在加载驱动类前需要导入mysql的jar包。
 * 这个步骤目的是为了调用DriverManager.registerDriver(driver);方法。
 * 为什么可以Class.forName就可以实现?是因为com.mysql.jdbc.Driver里面有静态代码块实现了注册自己到DriverManager方法。静态代码块会在加载类时执行。
 * 
 * JDBC4.0之后,每个驱动jar包的META-INF/services目录下提供了一个名为java.sql.Driver的文件。
 * 文件的内容就是该接口的实现类。所以加载驱动类步骤可以省略。低版本则不可以
 */
Class.forName("com.mysql.jdbc.Driver");
// 使用url username password 得到数据库连接
Connection conn = DriverManager.getConnection(url, username, password);

JDBC完成增、删、改、查

  • 获取数据库连接
  • 实现增、删、改(会修改表,返回语句所影响的行数)
  • 实现查(不修改表,返回查询结果)
    注意:记得在finally中关闭数据库资源。
public class Demo {
	public void fun() {
		/**
		 * 第一步:获取连接
		 * 通过DriverManager获取数据库连接
		 */
		// JDBC四大参数
		String driverClassName = "com.mysql.jdbc.Driver";
		// JDBC协议格式:jdbc:厂商名称:子协议(由厂商自定义)
		// MySQL子协议结构://主机:端口号/数据库名称
		String url = "jdbc:mysql://localhost:3306/mydb";
		String username = "root";
		String password = "123456";
		Connection conn = null;
		Statement stm = null;
		ResultSet rs = null;
		try {
			// 加载驱动类
			Class.forName(driverClassName);
			// 使用url username password 得到数据库连接
			conn = DriverManager.getConnection(url, username, password);
		
			/**
			 * 第二步:对数据库进行增、删、改
			 * 1、通过Connection对象创建Statement
			 * 2、调用int executeUpdate(String sql); 发送DML、DDL
			 */
			stm = conn.createStatement();
			
			// 增
			String sql = "INSERT INTO student VALUES('student_10', 'XiaoMing', 10, 'male')";
			int ret = stm.executeUpdate(sql);
			System.out.println(ret);
			
			// 改
			sql = "UPDATE student SET age=100, gender='male' WHERE name='XiaoMing'";
			ret = stm.executeUpdate(sql);
			System.out.println(ret);
			
			// 删
			sql = "DELETE FROM student";
			ret = stm.executeUpdate(sql);
			System.out.println(ret);
	
			/**
			 * 第三步:对数据库进行查操作
			 * 1、调用ResultSet rs = executeQuery(String querySql);
			 * 2、解析查询结果
			 */
			 // 查
			 sql = "SELECT * FROM student";
			 rs = stm.executeQuery(sql);
			 System.out.println(rs);
			
			/**
			 * 解析查询结果
			 * ResultSet内容有一个行光标
			 * 提供的next()方法可以将光标向下移动1行
			 * 提供的get方法可以获取数据,比如:
			 * getInt(1),getInt("name"),getString(1),getDouble(1) ...
			 * 1、把行光标移动到数据位置 boolean next(); 如果返回false则数据不存在。
			 */
			 while(rs.next()) {
			 	String number = rs.getString(1);
			 	String name = rs.getString("name");
			 	int age = rs.getInt("age");
			 	String gender = rs.getString("gender");
			 	System.out.println("number:" + number + " ,name:" + name + " ,age:"+age+" ,gender:" + gender);
			 }
		} catch(SQLException e) {
			e.printStackTrace();
		} catch(ClassNotFoundException e) {
			e.printStackTrace();
		} catch(Exception e) {
			e.printStackTrace();
		} finally {
			/**
			 * 四、关闭资源
			 */
			 if (rs != null) {
			 	rs.close();
			 }
			 if (stm != null) {
			 	stm.close();
			 }
			 if (conn != null) {
			 	conn.close();
			 }
		}
	}
}

结果集处理

ResultSet表示结果集,其内部维护一个行光标(游标)且提供了系列方法来移动光标。
结果集特性(当使用createStatement生成Statement时,结果集的特性就确定了):

  • 是否可滚动
  • 是否敏感
  • 是否可更新

得到结果集

Connection 的 createStatement 方法有两个实现,区分结果集是否可滚动:

  • Statement createStatement(); 生成的结果集:不滚动、不敏感、不可更新。
  • Statement createStatement(int,int);
    • 第一个参数:
      • ResultSet.TYPE_FORWARD_ONLY:不滚动结果集
      • ResultSet.TYPE_SCROLL_INSENSITIVE:滚动结果集,结果集数据不会再跟随数据库而变化
      • ResultSet.TYPE_SCROLL_SENSITIVE:滚动结果集,结果集数据会跟随数据库而变化(没有数据库驱动会支持它)
    • 第二个参数:
      • CONCUR_READ_ONLY:结果集只读,不能通过修改结果集反向影响数据库
      • CONCUR_UPDATABLE:结果集可更新,对结果集的更新可以反向影响数据库
        == Statement== 最重要的方法:
  • int executeUpdate(String sql); 执行更新操作,即 INSERT、UPDATE、DELETE 语句。这个方法也可以执行 CREATE TABLE 、ALTER TABLE、DROP TABLE等语句,只不过我们很少使用JDBC执行这些语句。
  • ResultSet executeQuery(String sql); 执行查询操作,查询操作返回ResultSet,即结果集。
  • boolean execute(String sql); 该方法可以执行增、删、改、查所有SQL语句。返回值为boolean类型,表示是否执行成功。如果使用该方法执行更新语句,则需要调用int getUpdateCount();来获取语句影响的行数。如果使用该方法执行查询语句,则需要调用ResultSet getResultSet();来获取查询结果。

结果集光标

结果集光标移动的前提是结果集是可滚动的。如果结果集不可滚动,则只可以使用next()移动光标。

移动行光标到固定位置

  • void beforeFirst();把光标移动到第一行数据的上面(表头,元数据列)。这是光标默认位置。
  • void afterLast();把光标移动到最后一行数据的下面。
  • boolean first();把光标移动到第一行数据位置,返回值表示移动是否成功。
  • boolean last();把光标移动到最后一行数据位置,返回值表示移动是否成功。

确认行光标位置

  • boolean isBeforeFirst();当前光标是否在第一行数据的上面。
  • boolean isAfterLast();当前光标是否在最后一行数据的下面。
  • boolean isFirst();当前光标是否指向第一行数据。
  • boolean isLast();当前光标是否指向最后一行数据。
  • int getRow();当前光标所指向的位置。

上下移动光标

  • boolean previous();把光标向上移动一行。
  • boolean next();把光标向下移动一行。
  • boolean relative(int row);相对移动,row为正数时表示向下移动row行,为负数时表示向上移动row行。
  • boolean absolute(int row);绝对位移,将光标移动到第row行。

结果集元数据

  • ResultSetMetaData getMetaData(); 得到元数据,由ResultSet提供。
  • int getColumnCount(); 获取结果集列数,由ResultSetMetaData提供。
  • String getColumnName(int colIndex); 获取指定列的列名,由ResultSetMetaData提供。

PreparedStatement

PreparedStatement是Statement的子接口。
他的优势在于:

  • 防SQL攻击
  • 提高代码可读性
  • 提高效率

PreparedStatement的使用

  • 给出SQL模板
  • 调用 Connection 的 PreparedStatement prepareStatement(String sql);
  • 调用 setXxx(); 方法设置参数
  • 调用 executeUpdate(); 或者 executeQuery(); 方法。
public class Demo {
	/**
	 * 演示SQL攻击,
	 * 正常输入 name + gender 可以正确输出查询到的学生信息。
	 * 但是如果输入的 name与gender 都是 "x' or 'a'='a" 则条件永远为真,查询到的是所有学生数据。
	 * 因为如果照这样输入则sql变成了:
	 * SELECT * FROM student WHERE name='x' or 'a'='a' and gender='x' or 'a'='a'
	 */
	public void fun(String name, String gender) {
		// JDBC四大参数
		String driverClassName = "com.mysql.jdbc.Driver";
		String url = "jdbc:mysql://localhost:3306/mydb";
		String username = "root";
		String password = "123456";
		
		Connection conn = null;
		Statement stm = null;
		ResultSet rs = null;
		try {
			// 加载驱动类
			Class.forName(driverClassName);
			// 使用url username password 得到数据库连接
			conn = DriverManager.getConnection(url, username, password);
			stm = conn.createStatement();
			// SQL模板,所有参数用?代替
			String sql = "SELECT * FROM student WHERE name='" + name + "' and gender='" + gender + "'";
			rs = stm.executeQuery(sql);
		} catch(SQLException e) {
			e.printStackTrace();
		} catch(ClassNotFoundException e) {
			e.printStackTrace();
		} catch(Exception e) {
			e.printStackTrace();
		} finally {
			 if (rs != null) {
			 	rs.close();
			 }
			 if (stm != null) {
			 	stm.close();
			 }
			 if (conn != null) {
			 	conn.close();
			 }
		}
	}
	
	public void fun1() {
		/**
		 * 第一步:获取连接
		 * 通过DriverManager获取数据库连接
		 */
		// JDBC四大参数
		String driverClassName = "com.mysql.jdbc.Driver";
		String url = "jdbc:mysql://localhost:3306/mydb";
		String username = "root";
		String password = "123456";
		
		Connection conn = null;
		PreparedStatement pstm = null;
		ResultSet rs = null;
		try {
			// 加载驱动类
			Class.forName(driverClassName);
			// 使用url username password 得到数据库连接
			conn = DriverManager.getConnection(url, username, password);
		
			// SQL模板,所有参数用?代替
			String sql = "SELECT * FROM student WHERE name=? and gender=?";
			pstm = conn.prepareStatement(sql);
			
			// 配置参数
			pstm.setString(1, name);
			pstm.setString(2, gender);

			// 执行查询
			rs = pstm.executeQuery();
		} catch(SQLException e) {
			e.printStackTrace();
		} catch(ClassNotFoundException e) {
			e.printStackTrace();
		} catch(Exception e) {
			e.printStackTrace();
		} finally {
			/**
			 * 四、关闭资源
			 */
			 if (rs != null) {
			 	rs.close();
			 }
			 if (stm != null) {
			 	stm.close();
			 }
			 if (conn != null) {
			 	conn.close();
			 }
		}
	}
}

预处理

SQL执行步骤:

  • 校验SQL语句语法
  • 编译:生成类似函数的东西
  • 执行:调用生成的函数
    PreparedStatement:
  • 前提:连接的数据库必须支持预处理(目前所有的数据几乎都支持)
  • 每个 pstm 都与一个SQL模板绑定。先把SQL模板给数据库校验,这样后续执行可以不需要再次校验(这就是为什么PreparedStatement可以提高效率)。
  • MySQL的预编译功能默认是关闭的,需要通过配置打开。配置参数(可以在url后面追加)有:useServerPrepStmts=true&cachePrepStmts=true
  • Statement也可以通过执行SQL指令的方式实现预编译。

JdbcUtils 1.0 获取数据库连接

public class JdbcUtil {
	private static Properties sProps = null;

	// 只需要在类加载时执行一次
	static {
		// 给sProps初始化,加载dbconfig.properties文件到props对象中
		try {
			InputStream in = JdbcUtil.class.getClassLoader()
			.getResourceAsStream("dbconfig.properties");
			sProps = new Properties();
			sProps.load(in);
		} catch(IOException e) {
			e.printStackTrace();
		}
		
		try {
			// 加载驱动类
			Class.forName(sProps.getProperty("driverClassName"));
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 获取数据库连接
	 */
	public static Connection getConnection() throws SQLExcetpion {
		return DriverManager.getConnection(sProps.getProperty("url"),
			sProps.getProperty("username"),
			sProps.getProperty("password"));
	}
}

时间类型转换

数据库类型与Java中类型的对应关系:

  • DATE ⇒ java.sql.Date
  • TIME ⇒ java.sql.Time
  • TIMESTAMP ⇒ java.sql.Timestamp

领域模型中属性不可以出现java.sql包下的数据。所有需要将java.util包下的数据类型与java.sql包下的数据类型转换。

  • java.sql.Date ⇒ java.util.Date、Time、Timestamp。不需要处理,因为:
    • java.sql.Date 是 java.util.Date 的子类
    • java.sql.Time 是 java.util.Time 的子类
    • java.sql.Timestamp 是 java.util.Timestamp 的子类
  • java.util.Date ⇒ java.sql.Data、Time、Timestamp
    • 将util的Date转换成毫秒值
    • 使用 long time 创建sql包下的对象

批处理

MySQL的批处理默认是关闭的。需要通过配置:rewriteBatchedStatements=true 打开批处理功能。可以将其添加到url尾部。

public class Demo {

	public void fun() throws SQLException {
		
		Connection conn = JdbcUtils.getConnection();
		String sql = "INSERT INTO student VALUES(?, ?, ?, ?)";
		PreparedStatement pstm = conn.prepareStatement(sql);
		
		// 插入批量数据
		for (int i = 0; i < 10000; i ++) {
			pstm.setInt(1, i + 1);
			pstm.setString(2, "student_" + i);
			pstm.setInt(3, i);
			pstm.setString(4, i % 2 == 0 ? "male" : "female");
			// 将本次参数插入批处理的集合
			pstm.addBatch();
		}
		// 执行批处理,不加rewriteBatchedStatements=true(不使用批处理)时,大概需要6分钟。使用批处理大概需要300ms
		pstm.executeBatch();
	}
}

关键字

createStatement() createStatement(int,int) executeUpdate(String sql) executeQuery(String sql) execute(String sql) getUpdateCount() getResultSet()

TYPE_FORWARD_ONLY TYPE_SCROLL_INSENSITIVE TYPE_SCROLL_SENSITIVE CONCUR_READ_ONLY CONCUR_UPDATABLE

beforeFirst() afterLast() first() last()
isBeforeFirst() isAfterLast() isFirst() isLast() getRow()
previous() next() relative(int row) absolute(int row)
getMetaData() getColumnCount() getColumnName(int colIndex)

prepareStatement(String sql)

addBatch executeBatch rewriteBatchedStatements=true

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