目录
3.2 创建statement或者preparedstatement接口,来执行SQL语句:
1.JDBC简介
JDBC:Java DataBase Connectivity
数据JavaEE开发技术之一
相关的API在JDK中:java.sql.* javax.sql.*
作用:编写数据库相关代码有一个统一标准
还需要数据库的驱动
二、JDBC的编码步骤
0、搭建开发环境:把数据库驱动jar包加入到应用的构建路径(classpath)
需要的jar包https://download.csdn.net/download/mocas_wang/12430202
1、注册驱动
2、获取与数据库的连接
3、创建代表SQL语句的对象
4、执行SQL语句
5、如果执行的查询语句,返回结果集,遍历结果集
6、释放占用的资源
三 JDBC代码实现
3.1 注册驱动,获取连接,完成查询操作
/*1 准备四大参数*/
String driveClassName="com.mysql.jdbc.Driver";
/*jdbc的格式有规范,固定格式,但数据库是自己创建的//jdbc协议:子协议://ip:端口号/数据库名*/
String url = "jdbc:mysql://localhost:3306/mysql2?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=UTC";//数据库自己创建,使用这个长字符串可以一劳永逸解决版本问题
//数据库用户名
String name = "root";//将要连接数据库的账户
String password = "*****";//将要连接数据库的密码
/*2 加载驱动类*/
Class.forName(driveClassName);
/* 3 利用DriverManager 得到connection*/
Connection con= DriverManager.getConnection(url,name,password);
/*
* 完成查询*/
/*1 通过connection创建statement*/
Statement stmt=con.createStatement();
/*2 调用statement的ResultSet rs=stmt.executeQuery()*/
ResultSet rs=stmt.executeQuery("select * from tb_stu");
System.out.println(rs);
/*3 解析ResultSet查询结果集*/
/*循环结果集,不断使用rs.next指向下一行*/
while (rs.next())
{
String number=rs.getString(1);//列索引
String ename=rs.getString("name");//列标签索引
int age=rs.getInt(3);
System.out.println(number+","+ename+","+age);
}
/*
* 关闭资源
* */
rs.close();
stmt.close();
con.close();
3.2 创建statement或者preparedstatement接口,来执行SQL语句:
使用statement接口:
statement接口创建完成后,可以执行SQL语句,对数据库的增删改查,其中查略显麻烦。在statement中使用字符串拼接的方式,该方式存在句法复杂容易出错的缺点,所以statement在实际使用中用的非常少。
字符串拼接方式的SQL语句是非常繁琐的,中间有很多的单引号,双引号极易出错。
Statement s = conn.createStatement();
//准备SQL语句
//注意:' 字符串要用单引号 '
String sql = "insert into t_course values (null,"+"'数学')";
//在Statement中使用字符串拼接的方式,存在诸多的问题
s.execute(sql);
System.out.println("执行插入语句成功!");
使用preparedstatement接口:
与statement一样,preparedStatement也是执行SQL的,不过需要根据SQL语句创建preparedstatement。除此之外,还能够通过设置参数来指定相应的值,而不是statement那样进行字符串的拼接。
/*1 得到连接对象*/
Connection con=jdbcUtils.getConnection();
/*2 给出sql模板,创建pstmt*/
String sql="update account set balance=balance+? where name=?";
PreparedStatement pstmt=con.prepareStatement(sql);
/*3 对参数赋值*/
pstmt.setDouble(1,balance);
pstmt.setString(2,ename);
/*4 执行*/
pstmt.executeUpdate();
四 JDBC中常用接口或类详解
Statement类讲解:
Jdbc程序中的Statement对象用于向数据库发送SQL语句, Statement对象常用方法:
Ø executeQuery(String sql) :用于向数据发送查询语句。 Ø executeUpdate(String sql):用于向数据库发送insert、update或delete语句 Ø execute(String sql):用于向数据库发送任意sql语句 Ø addBatch(String sql) :把多条sql语句放到一个批处理中。 Ø executeBatch():向数据库发送一批sql语句执行。
ResultSet类讲解:
l Jdbc程序中的ResultSet用于代表Sql语句的执行结果。Resultset封装执行结果时,采用的类似 于表格的方式。ResultSet 对象维护了一个指向表格数据行的游标,初始的时候,游标在第一 行之前,调用ResultSet.next() 方法,可以使游标指向具体的数据行,进行调用方法获取该行 的数据。
l ResultSet既然用于封装执行结果的,所以该对象提供的都是用于获取数据的get方法: l 获取任意类型的数据 : Ø getObject(int index) Ø getObject(string columnName) l 获取指定类型的数据 : Ø getString(int index) Ø getString(String columnName)
ResultSet还提供了对结果集进行滚动的方法:
Ø next():移动到下一行
Ø Previous():移动到前一行
Ø absolute(int row):移动到指定行
Ø beforeFirst():移动resultSet的最前面。
Ø afterLast() :移动到resultSet的最后面。
数据库的URL:
URL用于标识数据库的位置,通过URL地址告诉JDBC程序连接哪个数据库,URL的写法为:
依次为 : 协议 + 子协议 + 主机:端口 + 数据库 + 参数
常用数据库URL地址的写法:
Ø Oracle写法:jdbc:oracle:thin:@localhost:1521:sid Ø SqlServer写法:jdbc:microsoft:sqlserver://localhost:1433; DatabaseName=sid Ø MySql写法:jdbc:mysql://localhost:3306/sid
如果连接的是本地的Mysql数据库,并且连接使用的端口是3306 那么的url地址可以简写为: jdbc:mysql:///数据库
常用的url:jdbc:mysql://localhost:3306/mysql2?useUnicode=true&characterEncoding=utf8
jdbc:mysql://localhost:3306/mysql2?seUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&se
//创建向数据库发送sql的statement对象。
createcreateStatement()
//创建向数据库发送预编译sql的PrepareSatement对象。
prepareStatement(sql)
//创建执⾏存储过程的callableStatement对象
prepareCall(sql)
//设置事务⾃动提交
setAutoCommit(boolean autoCommit)
//提交事务
commit()
//回滚事务
rollback()
JDBC使⽤的⼀些细节
//模拟查询id为2的信息
String id = "2";
Connection connection = UtilsDemo.getConnection();
String sql = "SELECT * FROM users WHERE id = ?";
PreparedStatement preparedStatement =
connection.preparedStatement(sql);
//第⼀个参数表示第⼏个占位符【也就是?号】,第⼆个参数表示值是多少
preparedStatement.setString(1,id);
ResultSet resultSet = preparedStatement.executeQuery();
2.批处理
当需要向数据库发送⼀批SQL语句执⾏时,应避免向数据库⼀条条发送执⾏,采⽤批处理以提升执⾏效
率
批处理有两种⽅式:
1. Statement
2. PreparedStatement
通过executeBath()⽅法批量处理执⾏SQL语句,返回⼀个int[]数组,该数组代表各句SQL的返回值
以下代码是以Statement⽅式实现批处理
if (resultSet.next()) {
System.out.println(resultSet.getString("name"));
}
//释放资源
UtilsDemo.release(connection, preparedStatement, resultSet);
/*
* Statement执⾏批处理
*
* 优点:
* 可以向数据库发送不同的SQL语句
* 缺点:
* SQL没有预编译
* 仅参数不同的SQL,需要重复写多条SQL
* */
Connection connection = UtilsDemo.getConnection();
Statement statement = connection.createStatement();
String sql1 = "UPDATE users SET name='zhongfucheng' WHERE id='3'";
String sql2 = "INSERT INTO users (id, name, password, email,
birthday)" +
" VALUES('5','nihao','123','ss@qq.com','1995-12-1')";
//将sql添加到批处理
statement.addBatch(sql1);
statement.addBatch(sql2);
//执⾏批处理
statement.executeBatch();
//清空批处理的sql
statement.clearBatch();
UtilsDemo.release(connection, statement, null);
以下⽅式以PreparedStatement⽅式实现批处理
/*
* PreparedStatement批处理
* 优点:
* SQL语句预编译了
* 对于同⼀种类型的SQL语句,不⽤编写很多条
* 缺点:
* 不能发送不同类型的SQL语句
*
* */
Connection connection = UtilsDemo.getConnection();
String sql = "INSERT INTO test(id,name) VALUES (?,?)";
PreparedStatement preparedStatement =
connection.prepareStatement(sql);
for (int i = 1; i <= 205; i++) {
preparedStatement.setInt(1, i);
preparedStatement.setString(2, (i + "zhongfucheng"));
//添加到批处理中
preparedStatement.addBatch();
if (i %2 ==100) {
//执⾏批处理
preparedStatement.executeBatch();
//清空批处理【如果数据量太⼤,所有数据存⼊批处理,内存肯定溢出】
preparedStatement.clearBatch();
}
}
//不是所有的%2==100,剩下的再执⾏⼀次批处理
preparedStatement.executeBatch();
//再清空
preparedStatement.clearBatch();
UtilsDemo.release(connection, preparedStatement, null);
五 事务
事务概述
1、MySQL:事务默认是自动提交的。即一条语句就是出于独立的事务之中。
2、MySQL:SQL(TPL)
start transaction:开启事务。后面的语句处于同一个事务之中。
rollback:回滚。事务结束
commit:提交事务。事务结束
事务的特性
1、原子性:原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
2、一致性:事务必须使数据库从一个一致性状态变换到另外一个一致性状态。比如转账:转账前aaa+bbb=2000;转账后aaa+bbb=2000;
3、隔离性:事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。
4、持久性:指一个事务一旦被提交,它对数据库中数据的改变就是永久性的。
事务的隔离级别:
1、如果不考虑事务的隔离性,会出现什么问题?
脏读:一个线程中的事务读到了另一个线程中事务未提交的数据
不可重复读:一个线程中的事务读到了另一个线程中提交的update的数据,前后两次读到的内容不一致。
虚读:一个线程中的事务读到了另一个线程中提交的insert或delete的数据,前后读到的记录条数不一致。
2、事务的隔离级别:
READ UNCOMMITTED 脏读、不可重复读、虚读都有可能发生
READ COMMITTED 能避免脏读;不可重复读、虚读有可能发生(Oracle默认)
REPEATABLE READ 能避免脏读、不可重复读;虚读有可能发生(MySQL默认)
SERIALIZABLE 能避免脏读、不可重复读、虚读的发生
以上隔离级别:从上到下,级别越高,性能越低,数据也越安全。
优化代码:
Connection con=null;
/*对事务的处理必须使用connection对象*/
try {
/*开启事务
* .....
* 提交事务
* */
//开启连接
con=jdbcUtils.getConnection();
//开启事务
con.setAutoCommit(false);
/*处理.......*/
accountDao dao=new accountDao();
dao.updateBalance(con,from,-money);//转出金额
dao.updateBalance(con,to,+money);
//提交事务
con.commit();
con.close();
}
catch (Exception e)
{
/*要回滚事务*/
con.rollback();
con.close();
throw new RuntimeException(e);
}
六 工具类配置
上文多次用到了配置jdbcUtils工具类,源码如下
1 dbconfig.properties配置,此文件的配置路径在项目下
driveClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mysql2?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=UTC
name =root
password=wyh124413
2.jdbcUtils类
package commit;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
/**
* Created with IntelliJ IDEA
*
* @Author: mocas
* @Date: 2020/5/17 14:29
* @email: wangyuhang_mocas@163.com
*/
public class jdbcUtils {
private static Properties pro=null;
static {
try {
/*1 加载配置文件*/
InputStream in= jdbcUtils.class.getClassLoader().getResourceAsStream("dbconfig.properties");
pro=new Properties();
pro.load(in);
} catch (IOException e) {
throw new RuntimeException(e);
}
/*2 加载驱动类*/
try {
Class.forName(pro.getProperty("driveClassName"));
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
/*获取连接*/
public static Connection getConnection() throws IOException, ClassNotFoundException, SQLException {
/*3 得到connection*/
return DriverManager.getConnection(pro.getProperty("url"),pro.getProperty("name"),pro.getProperty("password"));
}
}