目录
JDBC概述:
基本介绍:
JDBC带来的好处:
JDBC API :
JDBC 快速入门:
获取数据库连接的五种方式:
方式一:
方式二:使用反射加载Driver类
方式三:使用DriverManger 替代 Diver 进行统一管理,便于应对需要连接多个数据库的场景。
方式四:使用Class.forName自动完成注册驱动,简化代码,这种方式使用最多,推荐使用
方式五:在方式四的基础上改进,增加配置文件,使连接数据库更灵活(最好的一种方式)
ResultSet [结果集]:
Statement:
SQL注入:
演示statement的注入问题:
PreparedStatement(预处理查询):
预处理的好处:
演示PreparedStatement的使用(预处理查询):
预处理的DML操作:
JDBC API小结:
JDBCUtils:
事务:
基本介绍:
应用实例:
批处理:
基本介绍:
应用实例:
批处理源码分析:
数据库连接池 :
5k 次连接数据库问题(使用传统方法):
传统获取Connection问题分析:
数据库连接池原理:
数据库连接池种类:
C3P0:
使用代码完成C3P0数据库连接池:
德鲁伊连接池(druid):
德鲁伊工具类:
Apache—DBUtils:
先分析一个问题:
土办法完成封装:(将结果集记录封装到ArrayList中)
Apache—DBUtils基本介绍:
应用实例:
查询1:(返回结果是多行多列的情况)
查询2:(返回结果是单行多列的情况)
查询3: (返回结果是单行单列的情况)
DML操作:
DAO 和增删改查通用方法-BasicDao:
先分析一个问题:
基本说明:
应用实例:
dao.utils:
dao.domain:
dao.dao:
Test.dao:
JDBC概述:
基本介绍:
JDBC带来的好处:
JDBC API :
JDBC 快速入门:
基本介绍:
JDBC带来的好处:
JDBC API :
创建Driver对象:
url:
//第一个jdbc程序,完成简单的操作
public class jbbc01 {
public static void main(String[] args) throws SQLException {
//1.注册驱动
Driver driver = new Driver();
//2.得到连接
String url = "jdbc:mysql://localhost:3306/db01";
//将用户名和密码放入到properties对象
//使用 Properties 类, 可以读写配置文件
Properties properties = new Properties();
//user 和 password是规定好的,不能乱改
properties.setProperty("user","root");//用户
properties.setProperty("password","zhx");//密码
Connection connect = driver.connect(url, properties);
//3.执行sql
String sql = "insert into dept values('50','kasldf','lskdfskl')";
Statement statement = connect.createStatement();//用于执行静态的sql语句,并返回其生成的结果的对象
int rows = statement.executeUpdate(sql); //如果是dml语句,返回的就是影响的行数,不成功返回0
//4.关闭连接资源
statement.close();
connect.close();
}
}
driver:
public interface Driver每个驱动程序类必须实现的接口。Java SQL框架允许多个数据库驱动程序。
每个驱动程序都应该提供一个实现Driver接口的类。
获取数据库连接的五种方式:
方式一:
//方式一
@Test
public void connect01() throws SQLException {
Driver driver = new Driver();
String url = "jdbc:mysql://localhost:3306/db01";
//将用户名和密码放入到properties对象 properties - 属性
Properties properties = new Properties();
//user 和 password是规定好的,不能乱改
properties.setProperty("user", "root");//用户
properties.setProperty("password", "zhx");//密码
Connection connect = driver.connect(url, properties);
System.out.println(connect);
}
方式二:使用反射加载Driver类
//方式二
@Test
public void connect02() throws ClassNotFoundException, InstantiationException, IllegalAccessException, SQLException {
//使用反射加载Driver类,动态加载,更加灵活,减少依赖性
Class> aClass = Class.forName("com.mysql.jdbc.Driver");
Driver driver = (Driver)aClass.newInstance();
String url = "jdbc:mysql://localhost:3306/db01";
//将用户名和密码放入到properties对象 properties - 属性
Properties properties = new Properties();
//user 和 password是规定好的,不能乱改
properties.setProperty("user", "root");//用户
properties.setProperty("password", "zhx");//密码
Connection connect = driver.connect(url, properties);
System.out.println(connect);
}
方式三:使用DriverManger 替代 Diver 进行统一管理,便于应对需要连接多个数据库的场景。
//方式三
@Test
public void connect03() throws ClassNotFoundException, InstantiationException, IllegalAccessException, SQLException {
//使用DriverManger 替代 Diver 进行统一管理,可以不用创建properties类
Class> aClass = Class.forName("com.mysql.jdbc.Driver");
Driver driver = (Driver)aClass.newInstance();
//创建url和user和password
String url = "jdbc:mysql://localhost:3306/db01";
String user = "root";
String password = "zhx";
DriverManager.registerDriver(driver);//注册Driver驱动
Connection connection = DriverManager.getConnection(url, user, password);
System.out.println(connection);
}
得到连接 Connection对象
方式四:使用Class.forName自动完成注册驱动,简化代码,这种方式使用最多,推荐使用
//方式四:使用class.forName 自动完成注册驱动,简化代码,不需要注册Driver驱动
@Test
public void connect04() throws ClassNotFoundException, SQLException {
//使用反射加载了Driver类
Class.forName("com.mysql.jdbc.Driver");
//创建url和user和password
String url = "jdbc:mysql://localhost:3306/db01";
String user = "root";
String password = "zhx";
Connection connection = DriverManager.getConnection(url, user, password);
System.out.println(connection);
}
为什么可以这样写呢?我们来看一下Driver的底层源码:底层已经帮我们注册了java驱动
方式五:在方式四的基础上改进,增加配置文件,使连接数据库更灵活(最好的一种方式)
@Test
public void connect05() throws IOException, ClassNotFoundException, SQLException {
//通过properties对象获取配置文件的信息
Properties properties = new Properties();
properties.load(new FileInputStream("src\mysql.properties"));
//获取相关的值
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String url = properties.getProperty("url");
String driver = properties.getProperty("driver");
Class.forName(driver);
Connection connection = DriverManager.getConnection(url, user, password);
System.out.println(connection);
}
配置文件:
properties对象加载API:
ResultSet [结果集]:
-
表示数据库结果集的数据表,通常通过执行查询数据库的语句生成。
-
ResultSet对象保持一个光标指向其当前的数据行。 最初,光标位于第一行之前。 next方法将光标移动到下一行,并且由于在ResultSet对象中没有更多行时返回false ,因此可以在while循环中使用循环来遍历结果集。
-
public class ResultSet_ {
public static void main(String[] args) throws IOException, ClassNotFoundException, SQLException {
//加载配置文件
Properties properties = new Properties();
properties.load(new FileInputStream("src\mysql.properties"));
//获取相关的值
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String driver = properties.getProperty("driver");
String url = properties.getProperty("url");
//注册驱动
Class.forName(driver);
//得到连接
Connection connection = DriverManager.getConnection(url, user, password);
//得到Statement
Statement statement = connection.createStatement();
//组织Sql
String sql = "select deptno,dname,loc from dept";
//执行给定的sql语句,该语句返回单个 ResultSet对象
ResultSet resultSet = statement.executeQuery(sql);
//使用while取出数据
while(resultSet.next()){//让光标向后移动,如果没有更多行,则返回false
int deptno = resultSet.getInt(1);//获取该行的第一列数据
String dname = resultSet.getString(2);
String loc = resultSet.getString(3);
System.out.println(deptno+"t"+dname+"t"+loc);
}
//关闭连接
resultSet.close();
statement.close();
connection.close();
}
}
debug代码 resultSet对象的结构:
Statement:
- 用于执行静态SQL语句并返回其生成的结果的对象。
- 默认情况下,每个Statement对象只能有一个ResultSet对象同时打开。 因此,如果一个ResultSet对象的读取与另一个对象的读取交错,则ResultSet对象必须由不同的Statement对象生成。 在所有执行方法Statement接口隐式关闭当前ResultSet声明的对象。
方法:
- executeQuery 可以执行查询语句
- executeUpdate不可以执行查询语句,只能执行增删改语句
SQL注入:
-- 演示 sql 注入
-- 创建一张表
CREATE TABLE admin ( -- 管理员表
NAME VARCHAr(32) NOT NULL UNIQUE,
pwd VARCHAr(32) NOT NULL DEFAULT '') CHARACTER SET utf8;
-- 添加数据
INSERT INTO admin VALUES('tom', '123');
-- 查找某个管理是否存在
SELECT *
FROM admin
WHERe NAME = 'tom' AND pwd = '123'
-- SQL注入
-- 输入用户名 为 1' or
-- 输入万能密码 为 or '1'= '1
SELECt *
FROM admin
WHERe NAME = '1' OR' AND pwd = 'OR '1'= '1'
SELECt * FROM admin
演示statement的注入问题:
public class Statement_ {
public static void main(String[] args) throws IOException, ClassNotFoundException, SQLException {
Scanner scanner = new Scanner(System.in);
//让用户输入管理员名和密码
System.out.print("请输入管理员的名字:");//next():当接受到空格或者 ' 就是表示结束
String admin_name = scanner.nextLine(); //如果希望看到SQL注入这里需要用到nextLine,nextLine遇到回车才算结束
System.out.print("请输入管理员的密码:");
String admin_pwd = scanner.nextLine();
//通过properties对象获取文件的信息
Properties properties = new Properties();
properties.load(new FileInputStream("src\mysql.properties"));
//获取相关的值
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String driver = properties.getProperty("driver");
String url = properties.getProperty("url");
//注册驱动
Class.forName(driver);
//得到连接
Connection connection = DriverManager.getConnection(url, user, password);
//得到Statement
Statement statement = connection.createStatement();
//组织Sql
String sql = "select name , pwd from admin where name ='" + admin_name + "' and pwd = '" + admin_pwd + "'";
ResultSet resultSet = statement.executeQuery(sql);
if (resultSet.next()) {//如果查询到一条记录,则说明该管理存在
System.out.println("登陆成功");
}else{
System.out.println("登录失败");
}
//关闭连接
resultSet.close();
statement.close();
connection.close();
}
}
这就很离谱了,按理说我们只有输入了正确的用户名和密码才能登录成功。这就出现了SQL注入问题。怎么解决呢?用预处理查询可以解决这个问题。
PreparedStatement(预处理查询):
预处理的好处:
演示PreparedStatement的使用(预处理查询):
public class PreparedStatement_ {
public static void main(String[] args) throws Exception {
Scanner scanner = new Scanner(System.in);
//让用户输入管理员名和密码
System.out.print("请输入管理员的名字:");//next():当接受到空格或者 ' 就是表示结束
String admin_name = scanner.nextLine(); //如果希望看到SQL注入这里需要用到nextLine,nextLine遇到回车才算结束
System.out.print("请输入管理员的密码:");
String admin_pwd = scanner.nextLine();
//通过properties对象获取文件的信息
Properties properties = new Properties();
properties.load(new FileInputStream("src\mysql.properties"));
//获取相关的值
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String driver = properties.getProperty("driver");
String url = properties.getProperty("url");
//注册驱动
Class.forName(driver);
//得到连接
Connection connection = DriverManager.getConnection(url, user, password);
//得到PreparedStatement
//组织Sql,?相当于占位符
String sql = "select name , pwd from admin where name =? and pwd = ?";
//prepareStatement是实现了PrepareStatement接口的实现类的对象
PreparedStatement preparedStatement = connection.prepareStatement(sql);
//给 ? 赋值
preparedStatement.setString(1,admin_name);
preparedStatement.setString(2,admin_pwd);
//执行select语句用 executeQuery
//执行 dml 用executeUpdate()
//这里执行executeQuery语句不用填入sql,因为前面已经赋值了
//如果填入相当于把select name , pwd from admin where name =? and pwd = ?填入,而问号无法识别。
ResultSet resultSet = preparedStatement.executeQuery();
if (resultSet.next()) {//如果查询到一条记录,则说明该管理存在
System.out.println("登陆成功");
} else {
System.out.println("登录失败");
}
//关闭连接
resultSet.close();
preparedStatement.close();
connection.close();
}
}
预处理的DML操作:
public class PreparedStatementDML {
public static void main(String[] args) throws Exception {
Scanner scanner = new Scanner(System.in);
//让用户输入管理员名和密码
// System.out.print("请输入添加管理员的名字:");//next():当接受到空格或者 ' 就是表示结束
// String admin_name = scanner.nextLine(); //如果希望看到SQL注入这里需要用到nextLine,nextLine遇到回车才算结束
// System.out.print("请输入添加管理员的密码:");
// String admin_pwd = scanner.nextLine();
System.out.print("请输入你要删除的人的名字:");
String admin_name = scanner.nextLine();
//通过properties对象获取文件的信息
Properties properties = new Properties();
properties.load(new FileInputStream("src\mysql.properties"));
//获取相关的值
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String driver = properties.getProperty("driver");
String url = properties.getProperty("url");
//注册驱动
Class.forName(driver);
//得到连接
Connection connection = DriverManager.getConnection(url, user, password);
//得到PreparedStatement
//组织Sql,?相当于占位符
//添加
//String sql = "insert into admin values(?,?)";
//修改
//String sql = "update admin set pwd = ? where name = ?";
//删除
String sql = "delete from admin where name = ?";
//prepareStatement是实现了PrepareStatement接口的实现类的对象
PreparedStatement preparedStatement = connection.prepareStatement(sql);
//给 ? 赋值
//preparedStatement.setString(1,admin_pwd);
//preparedStatement.setString(2, admin_name);
preparedStatement.setString(1,admin_name);
//执行select语句用 executeQuery
//执行 dml 用executeUpdate()
int i = preparedStatement.executeUpdate();
System.out.println(i > 0 ? "执行成功" : "执行失败");
//关闭连接
preparedStatement.close();
connection.close();
}
}
JDBC API小结:
JDBCUtils:
public class JDBCUtils {
//定义相关的属性,因为只需要一份,我们做成static
private static String user;//用户名
private static String password;//密码
private static String url;//url
private static String driver;//驱动名
//在static代码块中完成初始化
static {
try {
Properties properties = new Properties();
properties.load(new FileInputStream("src\mysql.properties"));
//读取相关的属性
user = properties.getProperty("user");
password = properties.getProperty("password");
url = properties.getProperty("url");
driver = properties.getProperty("driver");
} catch (IOException e) {
//在实际开发中,我们可以这样处理
//将编译异常转成运行异常
//这时调用者,可以选择捕获异常,也可以选择默认处理该异常,比较方便
throw new RuntimeException(e);
}
}
//连接数据库,返回Connection
public static Connection getConnection(){
try {
return DriverManager.getConnection(url,user,password);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
//关闭相关资源
//如果需要关闭资源,就传入对象,否则传入空
public static void close(ResultSet set, Statement statement,Connection connection){
//判断是否为空
try {
if(set != null){
set.close();
}
if(statement != null){
statement.close();
}
if(connection != null){
connection.close();
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
工具包的使用:
**
* 演示该类如何使用JDBCUtils工具类,完成dml 和 select
*/
public class JDBCUtils_Use {
@Test
public void testDML() {
//得到连接
Connection connection = null;
//组织sql
String sql = "update account set name = ? where id = ?";
PreparedStatement preparedStatement = null;
try {
connection = JDBCUtils.getConnection();
preparedStatement = connection.prepareStatement(sql);
//给占位符赋值
preparedStatement.setString(1, "周杰伦");
preparedStatement.setInt(2, 400);
//执行
preparedStatement.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
//关闭资源
JDBCUtils.close(null, preparedStatement, connection);
}
}
@Test
public void testSelect() {
//得到连接
Connection connection = null;
//组织sql
String sql = "select * from account where id = ?";
PreparedStatement preparedStatement = null;
ResultSet set = null;
try {
connection = JDBCUtils.getConnection();
preparedStatement = connection.prepareStatement(sql);
//给?赋值
preparedStatement.setInt(1,400);
//执行,得到结果集
set = preparedStatement.executeQuery();
//遍历结果集
while(set.next()){
int id = set.getInt("id");
String name = set.getString("name");
int money = set.getInt("money");
System.out.println(id + "t" + name + "t" +money);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
//关闭资源
JDBCUtils.close(set, preparedStatement, connection);
}
}
}
事务:
基本介绍:
方式一:
//方式一
@Test
public void connect01() throws SQLException {
Driver driver = new Driver();
String url = "jdbc:mysql://localhost:3306/db01";
//将用户名和密码放入到properties对象 properties - 属性
Properties properties = new Properties();
//user 和 password是规定好的,不能乱改
properties.setProperty("user", "root");//用户
properties.setProperty("password", "zhx");//密码
Connection connect = driver.connect(url, properties);
System.out.println(connect);
}
方式二:使用反射加载Driver类
//方式二
@Test
public void connect02() throws ClassNotFoundException, InstantiationException, IllegalAccessException, SQLException {
//使用反射加载Driver类,动态加载,更加灵活,减少依赖性
Class> aClass = Class.forName("com.mysql.jdbc.Driver");
Driver driver = (Driver)aClass.newInstance();
String url = "jdbc:mysql://localhost:3306/db01";
//将用户名和密码放入到properties对象 properties - 属性
Properties properties = new Properties();
//user 和 password是规定好的,不能乱改
properties.setProperty("user", "root");//用户
properties.setProperty("password", "zhx");//密码
Connection connect = driver.connect(url, properties);
System.out.println(connect);
}
方式三:使用DriverManger 替代 Diver 进行统一管理,便于应对需要连接多个数据库的场景。
//方式三
@Test
public void connect03() throws ClassNotFoundException, InstantiationException, IllegalAccessException, SQLException {
//使用DriverManger 替代 Diver 进行统一管理,可以不用创建properties类
Class> aClass = Class.forName("com.mysql.jdbc.Driver");
Driver driver = (Driver)aClass.newInstance();
//创建url和user和password
String url = "jdbc:mysql://localhost:3306/db01";
String user = "root";
String password = "zhx";
DriverManager.registerDriver(driver);//注册Driver驱动
Connection connection = DriverManager.getConnection(url, user, password);
System.out.println(connection);
}
得到连接 Connection对象
方式四:使用Class.forName自动完成注册驱动,简化代码,这种方式使用最多,推荐使用
//方式四:使用class.forName 自动完成注册驱动,简化代码,不需要注册Driver驱动
@Test
public void connect04() throws ClassNotFoundException, SQLException {
//使用反射加载了Driver类
Class.forName("com.mysql.jdbc.Driver");
//创建url和user和password
String url = "jdbc:mysql://localhost:3306/db01";
String user = "root";
String password = "zhx";
Connection connection = DriverManager.getConnection(url, user, password);
System.out.println(connection);
}
为什么可以这样写呢?我们来看一下Driver的底层源码:底层已经帮我们注册了java驱动
方式五:在方式四的基础上改进,增加配置文件,使连接数据库更灵活(最好的一种方式)
@Test
public void connect05() throws IOException, ClassNotFoundException, SQLException {
//通过properties对象获取配置文件的信息
Properties properties = new Properties();
properties.load(new FileInputStream("src\mysql.properties"));
//获取相关的值
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String url = properties.getProperty("url");
String driver = properties.getProperty("driver");
Class.forName(driver);
Connection connection = DriverManager.getConnection(url, user, password);
System.out.println(connection);
}
配置文件:
properties对象加载API:
ResultSet [结果集]:
-
表示数据库结果集的数据表,通常通过执行查询数据库的语句生成。
-
ResultSet对象保持一个光标指向其当前的数据行。 最初,光标位于第一行之前。 next方法将光标移动到下一行,并且由于在ResultSet对象中没有更多行时返回false ,因此可以在while循环中使用循环来遍历结果集。
-
表示数据库结果集的数据表,通常通过执行查询数据库的语句生成。
ResultSet对象保持一个光标指向其当前的数据行。 最初,光标位于第一行之前。 next方法将光标移动到下一行,并且由于在ResultSet对象中没有更多行时返回false ,因此可以在while循环中使用循环来遍历结果集。
debug代码 resultSet对象的结构:
Statement:
- 用于执行静态SQL语句并返回其生成的结果的对象。
- 默认情况下,每个Statement对象只能有一个ResultSet对象同时打开。 因此,如果一个ResultSet对象的读取与另一个对象的读取交错,则ResultSet对象必须由不同的Statement对象生成。 在所有执行方法Statement接口隐式关闭当前ResultSet声明的对象。
方法:
- executeQuery 可以执行查询语句
- executeUpdate不可以执行查询语句,只能执行增删改语句
SQL注入:
-- 演示 sql 注入
-- 创建一张表
CREATE TABLE admin ( -- 管理员表
NAME VARCHAr(32) NOT NULL UNIQUE,
pwd VARCHAr(32) NOT NULL DEFAULT '') CHARACTER SET utf8;
-- 添加数据
INSERT INTO admin VALUES('tom', '123');
-- 查找某个管理是否存在
SELECT *
FROM admin
WHERe NAME = 'tom' AND pwd = '123'
-- SQL注入
-- 输入用户名 为 1' or
-- 输入万能密码 为 or '1'= '1
SELECt *
FROM admin
WHERe NAME = '1' OR' AND pwd = 'OR '1'= '1'
SELECt * FROM admin
演示statement的注入问题:
public class Statement_ {
public static void main(String[] args) throws IOException, ClassNotFoundException, SQLException {
Scanner scanner = new Scanner(System.in);
//让用户输入管理员名和密码
System.out.print("请输入管理员的名字:");//next():当接受到空格或者 ' 就是表示结束
String admin_name = scanner.nextLine(); //如果希望看到SQL注入这里需要用到nextLine,nextLine遇到回车才算结束
System.out.print("请输入管理员的密码:");
String admin_pwd = scanner.nextLine();
//通过properties对象获取文件的信息
Properties properties = new Properties();
properties.load(new FileInputStream("src\mysql.properties"));
//获取相关的值
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String driver = properties.getProperty("driver");
String url = properties.getProperty("url");
//注册驱动
Class.forName(driver);
//得到连接
Connection connection = DriverManager.getConnection(url, user, password);
//得到Statement
Statement statement = connection.createStatement();
//组织Sql
String sql = "select name , pwd from admin where name ='" + admin_name + "' and pwd = '" + admin_pwd + "'";
ResultSet resultSet = statement.executeQuery(sql);
if (resultSet.next()) {//如果查询到一条记录,则说明该管理存在
System.out.println("登陆成功");
}else{
System.out.println("登录失败");
}
//关闭连接
resultSet.close();
statement.close();
connection.close();
}
}
这就很离谱了,按理说我们只有输入了正确的用户名和密码才能登录成功。这就出现了SQL注入问题。怎么解决呢?用预处理查询可以解决这个问题。
PreparedStatement(预处理查询):
预处理的好处:
演示PreparedStatement的使用(预处理查询):
public class PreparedStatement_ {
public static void main(String[] args) throws Exception {
Scanner scanner = new Scanner(System.in);
//让用户输入管理员名和密码
System.out.print("请输入管理员的名字:");//next():当接受到空格或者 ' 就是表示结束
String admin_name = scanner.nextLine(); //如果希望看到SQL注入这里需要用到nextLine,nextLine遇到回车才算结束
System.out.print("请输入管理员的密码:");
String admin_pwd = scanner.nextLine();
//通过properties对象获取文件的信息
Properties properties = new Properties();
properties.load(new FileInputStream("src\mysql.properties"));
//获取相关的值
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String driver = properties.getProperty("driver");
String url = properties.getProperty("url");
//注册驱动
Class.forName(driver);
//得到连接
Connection connection = DriverManager.getConnection(url, user, password);
//得到PreparedStatement
//组织Sql,?相当于占位符
String sql = "select name , pwd from admin where name =? and pwd = ?";
//prepareStatement是实现了PrepareStatement接口的实现类的对象
PreparedStatement preparedStatement = connection.prepareStatement(sql);
//给 ? 赋值
preparedStatement.setString(1,admin_name);
preparedStatement.setString(2,admin_pwd);
//执行select语句用 executeQuery
//执行 dml 用executeUpdate()
//这里执行executeQuery语句不用填入sql,因为前面已经赋值了
//如果填入相当于把select name , pwd from admin where name =? and pwd = ?填入,而问号无法识别。
ResultSet resultSet = preparedStatement.executeQuery();
if (resultSet.next()) {//如果查询到一条记录,则说明该管理存在
System.out.println("登陆成功");
} else {
System.out.println("登录失败");
}
//关闭连接
resultSet.close();
preparedStatement.close();
connection.close();
}
}
预处理的DML操作:
public class PreparedStatementDML {
public static void main(String[] args) throws Exception {
Scanner scanner = new Scanner(System.in);
//让用户输入管理员名和密码
// System.out.print("请输入添加管理员的名字:");//next():当接受到空格或者 ' 就是表示结束
// String admin_name = scanner.nextLine(); //如果希望看到SQL注入这里需要用到nextLine,nextLine遇到回车才算结束
// System.out.print("请输入添加管理员的密码:");
// String admin_pwd = scanner.nextLine();
System.out.print("请输入你要删除的人的名字:");
String admin_name = scanner.nextLine();
//通过properties对象获取文件的信息
Properties properties = new Properties();
properties.load(new FileInputStream("src\mysql.properties"));
//获取相关的值
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String driver = properties.getProperty("driver");
String url = properties.getProperty("url");
//注册驱动
Class.forName(driver);
//得到连接
Connection connection = DriverManager.getConnection(url, user, password);
//得到PreparedStatement
//组织Sql,?相当于占位符
//添加
//String sql = "insert into admin values(?,?)";
//修改
//String sql = "update admin set pwd = ? where name = ?";
//删除
String sql = "delete from admin where name = ?";
//prepareStatement是实现了PrepareStatement接口的实现类的对象
PreparedStatement preparedStatement = connection.prepareStatement(sql);
//给 ? 赋值
//preparedStatement.setString(1,admin_pwd);
//preparedStatement.setString(2, admin_name);
preparedStatement.setString(1,admin_name);
//执行select语句用 executeQuery
//执行 dml 用executeUpdate()
int i = preparedStatement.executeUpdate();
System.out.println(i > 0 ? "执行成功" : "执行失败");
//关闭连接
preparedStatement.close();
connection.close();
}
}
JDBC API小结:
JDBCUtils:
public class JDBCUtils {
//定义相关的属性,因为只需要一份,我们做成static
private static String user;//用户名
private static String password;//密码
private static String url;//url
private static String driver;//驱动名
//在static代码块中完成初始化
static {
try {
Properties properties = new Properties();
properties.load(new FileInputStream("src\mysql.properties"));
//读取相关的属性
user = properties.getProperty("user");
password = properties.getProperty("password");
url = properties.getProperty("url");
driver = properties.getProperty("driver");
} catch (IOException e) {
//在实际开发中,我们可以这样处理
//将编译异常转成运行异常
//这时调用者,可以选择捕获异常,也可以选择默认处理该异常,比较方便
throw new RuntimeException(e);
}
}
//连接数据库,返回Connection
public static Connection getConnection(){
try {
return DriverManager.getConnection(url,user,password);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
//关闭相关资源
//如果需要关闭资源,就传入对象,否则传入空
public static void close(ResultSet set, Statement statement,Connection connection){
//判断是否为空
try {
if(set != null){
set.close();
}
if(statement != null){
statement.close();
}
if(connection != null){
connection.close();
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
工具包的使用:
**
* 演示该类如何使用JDBCUtils工具类,完成dml 和 select
*/
public class JDBCUtils_Use {
@Test
public void testDML() {
//得到连接
Connection connection = null;
//组织sql
String sql = "update account set name = ? where id = ?";
PreparedStatement preparedStatement = null;
try {
connection = JDBCUtils.getConnection();
preparedStatement = connection.prepareStatement(sql);
//给占位符赋值
preparedStatement.setString(1, "周杰伦");
preparedStatement.setInt(2, 400);
//执行
preparedStatement.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
//关闭资源
JDBCUtils.close(null, preparedStatement, connection);
}
}
@Test
public void testSelect() {
//得到连接
Connection connection = null;
//组织sql
String sql = "select * from account where id = ?";
PreparedStatement preparedStatement = null;
ResultSet set = null;
try {
connection = JDBCUtils.getConnection();
preparedStatement = connection.prepareStatement(sql);
//给?赋值
preparedStatement.setInt(1,400);
//执行,得到结果集
set = preparedStatement.executeQuery();
//遍历结果集
while(set.next()){
int id = set.getInt("id");
String name = set.getString("name");
int money = set.getInt("money");
System.out.println(id + "t" + name + "t" +money);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
//关闭资源
JDBCUtils.close(set, preparedStatement, connection);
}
}
}
基本介绍:
应用实例:
模拟经典的转账业务:当一个人向另一个人转账100块时,一个人要增加一百元,另一个人要减少一百元,并且这两条语句要么都成功,要么都失败。
先创建一张表:
CREATE TABLE ACCOUNT( id INT PRIMARY KEY AUTO_INCREMENT, NAME VARCHAr(32) NOT NULL DEFAULT '', balance DOUBLE NOT NULL DEFAULT 0)CHARACTER SET utf8; INSERT INTO ACCOUNT VALUES(NULL,'马云',3000); INSERT INTO ACCOUNT VALUES(NULL,'马化腾',10000); SELECt * FROM ACCOUNT当我们不使用事务时,并且模拟发生异常的情况:
//没有使用事务. @Test public void noTransaction() { //操作转账的业务 //1. 得到连接 Connection connection = null; //2. 组织一个 sql String sql = "update account set balance = balance - 100 where id = 1"; String sql2 = "update account set balance = balance + 100 where id = 2"; PreparedStatement preparedStatement = null; //3. 创建 PreparedStatement 对象 try { connection = JDBCUtils.getConnection(); // 在默认情况下,connection 是默认自动提交 preparedStatement = connection.prepareStatement(sql); preparedStatement.executeUpdate(); // 执行第 1 条 sql int i = 1 / 0; //抛出异常 preparedStatement = connection.prepareStatement(sql2); preparedStatement.executeUpdate(); // 执行第 2 条 sql } catch (SQLException e) { e.printStackTrace(); } finally { //关闭资源 JDBCUtils.close(null, preparedStatement, connection); } }注意中间我们在两个执行语句的中间故意制造异常(int i = 1/0),这时有一条语句执行成功,这显然不是我们需要的:
用事务来处理:
public class Transaction_ { public static void main(String[] args) { //操作转账的业务 //1.得到连接 Connection connection = null; PreparedStatement preparedStatement = null; //2.组织一个sql String sql1 = "update account set balance = balance - 100 where id = 1"; String sql2 = "update account set balance = balance + 100 where id = 2"; //3.创建PrepareStatement对象 try { connection = JDBCUtils.getConnection(); //将connection设置为不自动提交 connection.setAutoCommit(false);//相当于开启了一个事务 preparedStatement = connection.prepareStatement(sql1); preparedStatement.executeUpdate();//执行第一条SQL语句 int i = 1/0; preparedStatement = connection.prepareStatement(sql2); preparedStatement.executeUpdate();//执行第二条sql语句 //最后提交事务 connection.commit(); } catch (SQLException e) { //如果出现异常,这里我们可以进行回滚,就是撤销执行的SQL、 System.out.println("执行发生了异常,撤销事务,进行回滚"); try { connection.rollback();//默认回滚到事务开始时的状态 } catch (SQLException ex) { ex.printStackTrace(); } e.printStackTrace(); } finally { JDBCUtils.close(null, preparedStatement, connection); } } }这里我们同样在两条语句中间制造异常,此时会发生回滚,这是我们想要的,要失败两个都失败:
去掉中间的异常语句:
总结重要的几条语句:
在异常里:
批处理:
基本介绍:
应用实例:
我们先创建一张表做测试:
CREATE TABLE admin2(
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAr(32) NOT NULL,
PASSWORD VARCHAr(32) NOT NULL);
先使用传统方法插入五千条数据,看看其执行效率:
@Test
//传统方法,添加5000条数据到admin2
public void noBatch() throws Exception {
Connection connection = JDBCUtils.getConnection();
String sql = "insert into admin2 values(null ,?,?)";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
System.out.println("开始执行");
long start = System.currentTimeMillis();//开始时间
for (int i = 0; i < 5000; i++) {
preparedStatement.setString(1,"jake" + i);
preparedStatement.setString(2,"666");
preparedStatement.executeUpdate();
}
long end = System.currentTimeMillis();
System.out.println("传统方式耗时="+(end - start));
//关闭连接
JDBCUtils.close(null,preparedStatement,connection);
}
运行结果:
使用批量方法,看看其效率:
//使用批量方法添加数据
@Test
public void batch() throws Exception{
Connection connection = JDBCUtils.getConnection();
String sql = "insert into admin2 values(null ,?,?)";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
System.out.println("开始执行");
long start = System.currentTimeMillis();//开始时间
for (int i = 0; i < 5000; i++) {
preparedStatement.setString(1,"jake" + i);
preparedStatement.setString(2,"666");
//将sql语句加入到批处理包中
preparedStatement.addBatch();
//当有5000条记录时,在批量执行
if((i + 1) % 1000 == 0){
preparedStatement.executeBatch();
//清空一把
preparedStatement.clearBatch();
}
}
long end = System.currentTimeMillis();
System.out.println("批量方式耗时="+(end - start));
//关闭连接
JDBCUtils.close(null,preparedStatement,connection);
}
注意需要修改配置文件:
运行结果:
几条重要语句:
批处理源码分析:
追进addBach方法中:
public void addBatch() throws SQLException {
synchronized(this.checkClosed().getConnectionMutex()) {
if (this.batchedArgs == null) {
this.batchedArgs = new ArrayList();
}
for(int i = 0; i < this.parameterValues.length; ++i) {
this.checkAllParametersSet(this.parameterValues[i], this.parameterStreams[i], i);
}
this.batchedArgs.add(new PreparedStatement.BatchParams(this.parameterValues, this.parameterStreams, this.isStream, this.streamLengths, this.isNull));
}
}
- 第一就创建 ArrayList - elementData => Object[]
- elementData => Object[] 就会存放我们预处理的 sql 语句
- 当 elementData 满后,就按照 1.5 扩容
- 当添加到指定的值后,就 executeBatch
- 批量处理会减少我们发送 sql 语句的网络开销,而且减少编译次数,因此效率提高
应用实例:
我们先创建一张表做测试:
CREATE TABLE admin2( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAr(32) NOT NULL, PASSWORD VARCHAr(32) NOT NULL);
先使用传统方法插入五千条数据,看看其执行效率:
@Test
//传统方法,添加5000条数据到admin2
public void noBatch() throws Exception {
Connection connection = JDBCUtils.getConnection();
String sql = "insert into admin2 values(null ,?,?)";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
System.out.println("开始执行");
long start = System.currentTimeMillis();//开始时间
for (int i = 0; i < 5000; i++) {
preparedStatement.setString(1,"jake" + i);
preparedStatement.setString(2,"666");
preparedStatement.executeUpdate();
}
long end = System.currentTimeMillis();
System.out.println("传统方式耗时="+(end - start));
//关闭连接
JDBCUtils.close(null,preparedStatement,connection);
}
运行结果:
使用批量方法,看看其效率:
//使用批量方法添加数据
@Test
public void batch() throws Exception{
Connection connection = JDBCUtils.getConnection();
String sql = "insert into admin2 values(null ,?,?)";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
System.out.println("开始执行");
long start = System.currentTimeMillis();//开始时间
for (int i = 0; i < 5000; i++) {
preparedStatement.setString(1,"jake" + i);
preparedStatement.setString(2,"666");
//将sql语句加入到批处理包中
preparedStatement.addBatch();
//当有5000条记录时,在批量执行
if((i + 1) % 1000 == 0){
preparedStatement.executeBatch();
//清空一把
preparedStatement.clearBatch();
}
}
long end = System.currentTimeMillis();
System.out.println("批量方式耗时="+(end - start));
//关闭连接
JDBCUtils.close(null,preparedStatement,connection);
}
注意需要修改配置文件:
运行结果:
几条重要语句:
批处理源码分析:
追进addBach方法中:
public void addBatch() throws SQLException {
synchronized(this.checkClosed().getConnectionMutex()) {
if (this.batchedArgs == null) {
this.batchedArgs = new ArrayList();
}
for(int i = 0; i < this.parameterValues.length; ++i) {
this.checkAllParametersSet(this.parameterValues[i], this.parameterStreams[i], i);
}
this.batchedArgs.add(new PreparedStatement.BatchParams(this.parameterValues, this.parameterStreams, this.isStream, this.streamLengths, this.isNull));
}
}
- 第一就创建 ArrayList - elementData => Object[]
- elementData => Object[] 就会存放我们预处理的 sql 语句
- 当 elementData 满后,就按照 1.5 扩容
- 当添加到指定的值后,就 executeBatch
- 批量处理会减少我们发送 sql 语句的网络开销,而且减少编译次数,因此效率提高
数据库连接池 :
5k 次连接数据库问题(使用传统方法):
@Test
//连接mysql五千次
public void testCon() {
//看看连接-关闭 Connection 会耗用多久
long start = System.currentTimeMillis();
System.out.println("开始连接");
for (int i = 0; i < 5000; i++) {
//使用传统的jdbc方式,得到连接
Connection connection = JDBCUtils.getConnection();
//做一些工作,比如得到preparedStatement,发送SQL
//...
//关闭
JDBCUtils.close(null,null,connection);
}
long end = System.currentTimeMillis();
System.out.println("传统方式5000次耗时="+(end - start));
}
传统获取Connection问题分析:
数据库连接池原理:
数据库连接池种类:
C3P0:
使用代码完成C3P0数据库连接池:
第一种方式,并测试效率,注意与传统方式对比:
//方式 1: 相关参数,在程序中指定 user, url , password 等
@Test
public void testC3P0_01() throws Exception {
//1. 创建一个数据源对象
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
//2. 通过配置文件 mysql.properties 获取相关连接的信息
Properties properties = new Properties();
properties.load(new FileInputStream("src\mysql.properties"));
//读取相关的属性值
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String url = properties.getProperty("url");
String driver = properties.getProperty("driver");
//给数据源 comboPooledDataSource 设置相关的参数
//注意:连接管理是由 comboPooledDataSource 来管理
comboPooledDataSource.setDriverClass(driver);
comboPooledDataSource.setJdbcUrl(url);
comboPooledDataSource.setUser(user);
comboPooledDataSource.setPassword(password);
//设置初始化连接数
comboPooledDataSource.setInitialPoolSize(10);
//最大连接数
comboPooledDataSource.setMaxPoolSize(50);
//测试连接池的效率,测试 mysql5000次 的操作
long start = System.currentTimeMillis();
for (int i = 0; i < 5000; i++) {
Connection connection = comboPooledDataSource.getConnection();
connection.close();
}
long end = System.currentTimeMillis();
System.out.println("c3p0 5000 次所需时间 = " +(end - start));
}
第二种方式,使用配置文件模板
//方式二:使用配置文件模板来完成
//1. 将c3p0 提供的c3p0.config.xml 拷贝到 src目录下
//2. 该文件指定了连接数据库和连接池的相关参数
@Test
public void testC3P0_02() throws SQLException {
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource("mysql");//参数是数据源名称,在配置文件中
//测试连接池的效率,测试 mysql5000次 的操作
long start = System.currentTimeMillis();
for (int i = 0; i < 5000; i++) {
Connection connection = comboPooledDataSource.getConnection();
connection.close();
}
long end = System.currentTimeMillis();
System.out.println("c3p0第二种方式 5000 次所需时间 = " + (end - start));
}
德鲁伊连接池(druid):
代码实现德鲁伊连接池并测试效率(运行五十万次):
@Test
public void testDruid() throws Exception {
//1. 加入Druid jar包
//2. 加入配置文件,将该文件拷贝到src 目录
//3.创建properties对象,读取配置文件
Properties properties = new Properties();
properties.load(new FileInputStream("src\druid.properties"));
//4.创建一个指定参数的数据库连接池
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
//测试连接池的效率,测试 mysql5000次 的操作
long start = System.currentTimeMillis();
for (int i = 0; i < 500000; i++) {
Connection connection = dataSource.getConnection();
connection.close();
}
long end = System.currentTimeMillis();
System.out.println("druid 500000 次所需时间 = " + (end - start));//408
}
配置文件:
运行结果:
我们同样让c3p0运行50万次,对比二者的效率:
重要代码:
德鲁伊工具类:
public class JDBCUtilsByDruid {
private static DataSource ds;
//使用静态代码块完成初始化
static {
Properties properties = new Properties();
try {
properties.load(new FileInputStream("src\druid.properties"));
ds = DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
}
}
//编写getConnection方法
public static Connection getConnection() throws SQLException {
return ds.getConnection();
}
//关闭连接,(把连接放回到连接池)
public static void close(ResultSet resultSet, Statement statement,Connection connection){
try {
if(resultSet != null){
resultSet.close();
}
if(statement != null){
statement.close();
}
if(connection != null){
connection.close();
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
5k 次连接数据库问题(使用传统方法):
@Test
//连接mysql五千次
public void testCon() {
//看看连接-关闭 Connection 会耗用多久
long start = System.currentTimeMillis();
System.out.println("开始连接");
for (int i = 0; i < 5000; i++) {
//使用传统的jdbc方式,得到连接
Connection connection = JDBCUtils.getConnection();
//做一些工作,比如得到preparedStatement,发送SQL
//...
//关闭
JDBCUtils.close(null,null,connection);
}
long end = System.currentTimeMillis();
System.out.println("传统方式5000次耗时="+(end - start));
}
传统获取Connection问题分析:
数据库连接池原理:
数据库连接池种类:
C3P0:
使用代码完成C3P0数据库连接池:
第一种方式,并测试效率,注意与传统方式对比:
//方式 1: 相关参数,在程序中指定 user, url , password 等
@Test
public void testC3P0_01() throws Exception {
//1. 创建一个数据源对象
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
//2. 通过配置文件 mysql.properties 获取相关连接的信息
Properties properties = new Properties();
properties.load(new FileInputStream("src\mysql.properties"));
//读取相关的属性值
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String url = properties.getProperty("url");
String driver = properties.getProperty("driver");
//给数据源 comboPooledDataSource 设置相关的参数
//注意:连接管理是由 comboPooledDataSource 来管理
comboPooledDataSource.setDriverClass(driver);
comboPooledDataSource.setJdbcUrl(url);
comboPooledDataSource.setUser(user);
comboPooledDataSource.setPassword(password);
//设置初始化连接数
comboPooledDataSource.setInitialPoolSize(10);
//最大连接数
comboPooledDataSource.setMaxPoolSize(50);
//测试连接池的效率,测试 mysql5000次 的操作
long start = System.currentTimeMillis();
for (int i = 0; i < 5000; i++) {
Connection connection = comboPooledDataSource.getConnection();
connection.close();
}
long end = System.currentTimeMillis();
System.out.println("c3p0 5000 次所需时间 = " +(end - start));
}
第二种方式,使用配置文件模板
//方式二:使用配置文件模板来完成
//1. 将c3p0 提供的c3p0.config.xml 拷贝到 src目录下
//2. 该文件指定了连接数据库和连接池的相关参数
@Test
public void testC3P0_02() throws SQLException {
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource("mysql");//参数是数据源名称,在配置文件中
//测试连接池的效率,测试 mysql5000次 的操作
long start = System.currentTimeMillis();
for (int i = 0; i < 5000; i++) {
Connection connection = comboPooledDataSource.getConnection();
connection.close();
}
long end = System.currentTimeMillis();
System.out.println("c3p0第二种方式 5000 次所需时间 = " + (end - start));
}
德鲁伊连接池(druid):
代码实现德鲁伊连接池并测试效率(运行五十万次):
@Test
public void testDruid() throws Exception {
//1. 加入Druid jar包
//2. 加入配置文件,将该文件拷贝到src 目录
//3.创建properties对象,读取配置文件
Properties properties = new Properties();
properties.load(new FileInputStream("src\druid.properties"));
//4.创建一个指定参数的数据库连接池
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
//测试连接池的效率,测试 mysql5000次 的操作
long start = System.currentTimeMillis();
for (int i = 0; i < 500000; i++) {
Connection connection = dataSource.getConnection();
connection.close();
}
long end = System.currentTimeMillis();
System.out.println("druid 500000 次所需时间 = " + (end - start));//408
}
配置文件:
运行结果:
我们同样让c3p0运行50万次,对比二者的效率:
重要代码:
public class JDBCUtilsByDruid {
private static DataSource ds;
//使用静态代码块完成初始化
static {
Properties properties = new Properties();
try {
properties.load(new FileInputStream("src\druid.properties"));
ds = DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
}
}
//编写getConnection方法
public static Connection getConnection() throws SQLException {
return ds.getConnection();
}
//关闭连接,(把连接放回到连接池)
public static void close(ResultSet resultSet, Statement statement,Connection connection){
try {
if(resultSet != null){
resultSet.close();
}
if(statement != null){
statement.close();
}
if(connection != null){
connection.close();
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
德鲁伊工具类使用:
@Test
public void testSelect() {
//得到连接
Connection connection = null;
//组织sql
String sql = "select * from account where id = ?";
PreparedStatement preparedStatement = null;
ResultSet set = null;
try {
connection = JDBCUtilsByDruid.getConnection();
preparedStatement = connection.prepareStatement(sql);
//给?赋值
preparedStatement.setInt(1, 1);
//执行,得到结果集
set = preparedStatement.executeQuery();
//遍历结果集
while (set.next()) {
int id = set.getInt("id");
String name = set.getString("name");
Double balance = set.getDouble("balance");
System.out.println(id + "t" + name + "t" + balance);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
//关闭资源
JDBCUtilsByDruid.close(set, preparedStatement, connection);
}
}
Apache—DBUtils:
先分析一个问题:
土办法完成封装:(将结果集记录封装到ArrayList中)
//使用土方法来解决ResultSet = 封装 ->ArrayList
@Test
public ArrayList testSelectToArrayList() {
//得到连接
Connection connection = null;
//组织sql
String sql = "select * from actor where id >= ?";
PreparedStatement preparedStatement = null;
ResultSet set = null;
//创建ArrayList对象,存放actor对象
ArrayList list = new ArrayList();
try {
connection = JDBCUtilsByDruid.getConnection();
preparedStatement = connection.prepareStatement(sql);
//给?赋值
preparedStatement.setInt(1, 1);
//执行,得到结果集
set = preparedStatement.executeQuery();
//遍历结果集
while (set.next()) {
int id = set.getInt("id");
String name = set.getString("name");
String sex = set.getString("sex");
Date borndate = set.getDate("borndate");
int phone = set.getInt("phone");
//把得到的resultSet的记录,封装到Actor对象,放入到list集合
list.add(new Actor(id, name, sex, borndate, phone));
}
System.out.println(list);
} catch (SQLException e) {
e.printStackTrace();
} finally {
//关闭资源
JDBCUtilsByDruid.close(set, preparedStatement, connection);
}
//因为ArryList 和 connection没有任何关联,所以该集合可以复用;
return list;
}
Apache—DBUtils基本介绍:
应用实例:
查询1:(返回结果是多行多列的情况)
@Test
public void testQueryMany() throws SQLException {//返回结果是多行的情况
//1. 得到连接
Connection connection = JDBCUtilsByDruid.getConnection();
//2. 使用 DBUtils 类和接口,先引入DBUtils相关的jar文件,加入到项目中
//3. 创建QueryRunner
QueryRunner queryRunner = new QueryRunner();
//4. 就可以执行相关的方法,返回ArrayList结果集
//String sql = "select * from actor where id > ?";
//注意:sql语句也可以查询部分列
String sql = "select id,name from actor where id >= ?";
//(1). query 方法就是执行sql语句,得到resultSet --封装到ArrayList集合中
//(2). 返回集合
//(3). connection :连接
//(4). sql:执行的sql语句
//(5). new BeanListHandler<>(Actor.class): 在将resultSet -> Actor对象 -> 封装到ArrayList
// 底层使用反射机制 去获取Actor类的属性,进行封装
//(6). 1 就是给sql语句中的?赋值,可以有多个值,因为他是可变参数
//(7). 底层得到的resultSet,会在query关闭,PreparedStatement也在底层创建并关闭
List list =
queryRunner.query(connection, sql, new BeanListHandler<>(Actor.class), 1);
for (Actor actor : list) {
System.out.println(actor);
}
//释放资源
JDBCUtilsByDruid.close(null, null, connection);
}
重要代码,注意对比:
运行结果:
查询2:(返回结果是单行多列的情况)
@Test
//演示 apache + dbutils + druid 完成 返回的结果是单行记录,单个对象
public void testQuerySingle() throws SQLException {
//1. 得到连接
Connection connection = JDBCUtilsByDruid.getConnection();
//2. 使用 DBUtils 类和接口,先引入DBUtils相关的jar文件,加入到项目中
//3. 创建QueryRunner
QueryRunner queryRunner = new QueryRunner();
//4. 就可以执行相关的方法,返回单个对象
String sql = "select id,name from actor where id = ?";
//因为我们知道我们返回的单行多列记录<--> 单个对象, 使用的Handler 是BeanHandler
Actor actor = queryRunner.query(connection, sql, new BeanHandler<>(Actor.class), 4);
System.out.println(actor);
//释放资源
JDBCUtilsByDruid.close(null,null,connection);
}
运行结果:
查询3: (返回结果是单行单列的情况)
@Test
//演示 apache + dbutils + druid 完成 是单行单列的情况 - 返回的就是Object对象
public void testScalar() throws SQLException {
//1. 得到连接
Connection connection = JDBCUtilsByDruid.getConnection();
//2. 使用 DBUtils 类和接口,先引入DBUtils相关的jar文件,加入到项目中
//3. 创建QueryRunner
QueryRunner queryRunner = new QueryRunner();
//4. 就可以执行相关的方法,返回单行单列,返回的就是Object
String sql = "select name from actor where id = ?";
//因为返回的是一个对象,使用的handle 就是 ScalarHandler
Object query = queryRunner.query(connection, sql, new ScalarHandler(), 4);
System.out.println(query);
//释放资源
JDBCUtilsByDruid.close(null,null,connection);
}
运行结果:
DML操作:
@Test
//演示 apache-dbutils + druid 完成 dml(update,insert,delete)
public void testDML() throws SQLException {
Connection connection = JDBCUtilsByDruid.getConnection();
QueryRunner queryRunner = new QueryRunner();
//修改
//String sql = "update actor set name = ? where id = ?";
//添加
//String sql = "insert into actor values(null,?,?,?,?)";
//删除
String sql = "delete from actor where id = ?";
//执行 DML 操作都是 queryRunner.update()
//返回值是受影响的行数 -》 affectedRow
//int affectedRow = queryRunner.update(connection, sql,"张三丰",4);
//int affectedRow = queryRunner.update(connection, sql, "林青霞", "女","1966-10-10",119);
int affectedRow = queryRunner.update(connection, sql, 5);
System.out.println(affectedRow > 0 ? "执行成功" : "执行没有影响到数据库");
JDBCUtilsByDruid.close(null,null,connection);
}
DAO 和增删改查通用方法-BasicDao:
先分析一个问题:
基本说明:
应用实例:
完成一个简单设计:
dao.utils ----工具类
dao.domain ----javabean
dao.dao ----存放XxxDao和BasicDAO
dao.test ----写测试类
dao.utils:
public class JDBCUtilsByDruid {
private static DataSource ds;
//使用静态代码块完成初始化
static {
Properties properties = new Properties();
try {
properties.load(new FileInputStream("src\druid.properties"));
ds = DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
}
}
//编写getConnection方法
public static Connection getConnection() throws SQLException {
return ds.getConnection();
}
//关闭连接,(把连接放回到连接池)
public static void close(ResultSet resultSet, Statement statement,Connection connection){
try {
if(resultSet != null){
resultSet.close();
}
if(statement != null){
statement.close();
}
if(connection != null){
connection.close();
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
dao.domain:
public class Actor {
private Integer id;
private String name;
private String sex;
private Date borndate; //用Util包下的date
private Integer phone;
public Actor() {//一定要给一个无参构造器,反射需要
}
public Actor(Integer id, String name, String sex, Date borndate, Integer phone) {
this.id = id;
this.name = name;
this.sex = sex;
this.borndate = borndate;
this.phone = phone;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Date getBorndate() {
return borndate;
}
public void setBorndate(Date borndate) {
this.borndate = borndate;
}
public Integer getPhone() {
return phone;
}
public void setPhone(Integer phone) {
this.phone = phone;
}
@Override
public String toString() {
return "Actor{" +
" + id +
", name='" + name + ''' +
", sex='" + sex + ''' +
", borndate=" + borndate +
", phone=" + phone +
'}';
}
}
dao.dao:
public class BasicDAO {//泛型指定具体的类型
private QueryRunner qr = new QueryRunner();
//开发通用的DML方法,针对任意的表
//Object... parameters给sql语句中的?补充相应的值
public int update(String sql,Object... parameters){
Connection connection = null;
try {
connection = JDBCUtilsByDruid.getConnection();
int update = qr.update(connection, sql, parameters);
return update;
} catch (SQLException e) {
throw new RuntimeException(e); //将编译异常 -> 运行异常,抛出
}finally {
JDBCUtilsByDruid.close(null,null,connection);
}
}
//返回多个对象(及查询的结果是多行),针对任意表
public List queryMulti(String sql,Class tClass, Object... parameters){
Connection connection = null;
try {
connection = JDBCUtilsByDruid.getConnection();
return qr.query(connection, sql, new BeanListHandler(tClass), parameters);
} catch (SQLException e) {
throw new RuntimeException(e); //将编译异常 -> 运行异常,抛出
}finally {
JDBCUtilsByDruid.close(null,null,connection);
}
}
//查询单行结果的通用方法
public T querySingle(String sql,Class tClass,Object... parameters){
Connection connection = null;
try {
connection = JDBCUtilsByDruid.getConnection();
return qr.query(connection,sql,new BeanHandler(tClass),parameters);
} catch (SQLException e) {
throw new RuntimeException(e); //将编译异常 -> 运行异常,抛出
}finally {
JDBCUtilsByDruid.close(null,null,connection);
}
}
//查询单行单列的方法,即返回单值的方法
public Object queryScalar(String sql,Object... parameters){
Connection connection = null;
try {
connection = JDBCUtilsByDruid.getConnection();
return qr.query(connection, sql, new ScalarHandler(), parameters);
} catch (SQLException e) {
throw new RuntimeException(e); //将编译异常 -> 运行异常,抛出
}finally {
JDBCUtilsByDruid.close(null,null,connection);
}
}
}
public class ActorDAO extends BasicDAO{
//1. 继承了BasicDAO,有了BasicDAO的方法
//2. 根据业务需求,可以编写特有的方法
}
Test.dao:
public class TestDAO {
//测试ActorDAO 对 actor表的crud操作
@Test
public void testActorDAO(){
ActorDAO actorDAO = new ActorDAO();
//1. 查询多行记录
List actors = actorDAO.queryMulti("select * from actor where id >= ?", Actor.class, 4);
for (Actor actor : actors) {
System.out.println(actor);
}
//2. 查询单行记录
Actor actor = actorDAO.querySingle("select * from actor where id = ?", Actor.class, 4);
System.out.println(actor);
//3. 查询单行单列
Object o = actorDAO.queryScalar("select * from actor where id = ?", 4);
System.out.println(o);
//4. dml 操作
int update = actorDAO.update("insert into actor values(null,?,?,?,?)", "张无忌", "男", "2000-11-11", 900);
System.out.println(update > 0? "执行成功" : "执行没有影响表");
}
}
先分析一个问题:
土办法完成封装:(将结果集记录封装到ArrayList中)
//使用土方法来解决ResultSet = 封装 ->ArrayList
@Test
public ArrayList testSelectToArrayList() {
//得到连接
Connection connection = null;
//组织sql
String sql = "select * from actor where id >= ?";
PreparedStatement preparedStatement = null;
ResultSet set = null;
//创建ArrayList对象,存放actor对象
ArrayList list = new ArrayList();
try {
connection = JDBCUtilsByDruid.getConnection();
preparedStatement = connection.prepareStatement(sql);
//给?赋值
preparedStatement.setInt(1, 1);
//执行,得到结果集
set = preparedStatement.executeQuery();
//遍历结果集
while (set.next()) {
int id = set.getInt("id");
String name = set.getString("name");
String sex = set.getString("sex");
Date borndate = set.getDate("borndate");
int phone = set.getInt("phone");
//把得到的resultSet的记录,封装到Actor对象,放入到list集合
list.add(new Actor(id, name, sex, borndate, phone));
}
System.out.println(list);
} catch (SQLException e) {
e.printStackTrace();
} finally {
//关闭资源
JDBCUtilsByDruid.close(set, preparedStatement, connection);
}
//因为ArryList 和 connection没有任何关联,所以该集合可以复用;
return list;
}
Apache—DBUtils基本介绍:
查询1:(返回结果是多行多列的情况)
@Test
public void testQueryMany() throws SQLException {//返回结果是多行的情况
//1. 得到连接
Connection connection = JDBCUtilsByDruid.getConnection();
//2. 使用 DBUtils 类和接口,先引入DBUtils相关的jar文件,加入到项目中
//3. 创建QueryRunner
QueryRunner queryRunner = new QueryRunner();
//4. 就可以执行相关的方法,返回ArrayList结果集
//String sql = "select * from actor where id > ?";
//注意:sql语句也可以查询部分列
String sql = "select id,name from actor where id >= ?";
//(1). query 方法就是执行sql语句,得到resultSet --封装到ArrayList集合中
//(2). 返回集合
//(3). connection :连接
//(4). sql:执行的sql语句
//(5). new BeanListHandler<>(Actor.class): 在将resultSet -> Actor对象 -> 封装到ArrayList
// 底层使用反射机制 去获取Actor类的属性,进行封装
//(6). 1 就是给sql语句中的?赋值,可以有多个值,因为他是可变参数
//(7). 底层得到的resultSet,会在query关闭,PreparedStatement也在底层创建并关闭
List list =
queryRunner.query(connection, sql, new BeanListHandler<>(Actor.class), 1);
for (Actor actor : list) {
System.out.println(actor);
}
//释放资源
JDBCUtilsByDruid.close(null, null, connection);
}
重要代码,注意对比:
运行结果:
查询2:(返回结果是单行多列的情况)
@Test
//演示 apache + dbutils + druid 完成 返回的结果是单行记录,单个对象
public void testQuerySingle() throws SQLException {
//1. 得到连接
Connection connection = JDBCUtilsByDruid.getConnection();
//2. 使用 DBUtils 类和接口,先引入DBUtils相关的jar文件,加入到项目中
//3. 创建QueryRunner
QueryRunner queryRunner = new QueryRunner();
//4. 就可以执行相关的方法,返回单个对象
String sql = "select id,name from actor where id = ?";
//因为我们知道我们返回的单行多列记录<--> 单个对象, 使用的Handler 是BeanHandler
Actor actor = queryRunner.query(connection, sql, new BeanHandler<>(Actor.class), 4);
System.out.println(actor);
//释放资源
JDBCUtilsByDruid.close(null,null,connection);
}
运行结果:
查询3: (返回结果是单行单列的情况)
@Test
//演示 apache + dbutils + druid 完成 是单行单列的情况 - 返回的就是Object对象
public void testScalar() throws SQLException {
//1. 得到连接
Connection connection = JDBCUtilsByDruid.getConnection();
//2. 使用 DBUtils 类和接口,先引入DBUtils相关的jar文件,加入到项目中
//3. 创建QueryRunner
QueryRunner queryRunner = new QueryRunner();
//4. 就可以执行相关的方法,返回单行单列,返回的就是Object
String sql = "select name from actor where id = ?";
//因为返回的是一个对象,使用的handle 就是 ScalarHandler
Object query = queryRunner.query(connection, sql, new ScalarHandler(), 4);
System.out.println(query);
//释放资源
JDBCUtilsByDruid.close(null,null,connection);
}
运行结果:
DML操作:
@Test
//演示 apache-dbutils + druid 完成 dml(update,insert,delete)
public void testDML() throws SQLException {
Connection connection = JDBCUtilsByDruid.getConnection();
QueryRunner queryRunner = new QueryRunner();
//修改
//String sql = "update actor set name = ? where id = ?";
//添加
//String sql = "insert into actor values(null,?,?,?,?)";
//删除
String sql = "delete from actor where id = ?";
//执行 DML 操作都是 queryRunner.update()
//返回值是受影响的行数 -》 affectedRow
//int affectedRow = queryRunner.update(connection, sql,"张三丰",4);
//int affectedRow = queryRunner.update(connection, sql, "林青霞", "女","1966-10-10",119);
int affectedRow = queryRunner.update(connection, sql, 5);
System.out.println(affectedRow > 0 ? "执行成功" : "执行没有影响到数据库");
JDBCUtilsByDruid.close(null,null,connection);
}
先分析一个问题:
应用实例:基本说明:
完成一个简单设计:
dao.utils ----工具类
dao.domain ----javabean
dao.dao ----存放XxxDao和BasicDAO
dao.test ----写测试类
dao.utils:
public class JDBCUtilsByDruid {
private static DataSource ds;
//使用静态代码块完成初始化
static {
Properties properties = new Properties();
try {
properties.load(new FileInputStream("src\druid.properties"));
ds = DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
}
}
//编写getConnection方法
public static Connection getConnection() throws SQLException {
return ds.getConnection();
}
//关闭连接,(把连接放回到连接池)
public static void close(ResultSet resultSet, Statement statement,Connection connection){
try {
if(resultSet != null){
resultSet.close();
}
if(statement != null){
statement.close();
}
if(connection != null){
connection.close();
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
dao.domain:
public class Actor {
private Integer id;
private String name;
private String sex;
private Date borndate; //用Util包下的date
private Integer phone;
public Actor() {//一定要给一个无参构造器,反射需要
}
public Actor(Integer id, String name, String sex, Date borndate, Integer phone) {
this.id = id;
this.name = name;
this.sex = sex;
this.borndate = borndate;
this.phone = phone;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Date getBorndate() {
return borndate;
}
public void setBorndate(Date borndate) {
this.borndate = borndate;
}
public Integer getPhone() {
return phone;
}
public void setPhone(Integer phone) {
this.phone = phone;
}
@Override
public String toString() {
return "Actor{" +
" + id +
", name='" + name + ''' +
", sex='" + sex + ''' +
", borndate=" + borndate +
", phone=" + phone +
'}';
}
}
dao.dao:
public class BasicDAO {//泛型指定具体的类型
private QueryRunner qr = new QueryRunner();
//开发通用的DML方法,针对任意的表
//Object... parameters给sql语句中的?补充相应的值
public int update(String sql,Object... parameters){
Connection connection = null;
try {
connection = JDBCUtilsByDruid.getConnection();
int update = qr.update(connection, sql, parameters);
return update;
} catch (SQLException e) {
throw new RuntimeException(e); //将编译异常 -> 运行异常,抛出
}finally {
JDBCUtilsByDruid.close(null,null,connection);
}
}
//返回多个对象(及查询的结果是多行),针对任意表
public List queryMulti(String sql,Class tClass, Object... parameters){
Connection connection = null;
try {
connection = JDBCUtilsByDruid.getConnection();
return qr.query(connection, sql, new BeanListHandler(tClass), parameters);
} catch (SQLException e) {
throw new RuntimeException(e); //将编译异常 -> 运行异常,抛出
}finally {
JDBCUtilsByDruid.close(null,null,connection);
}
}
//查询单行结果的通用方法
public T querySingle(String sql,Class tClass,Object... parameters){
Connection connection = null;
try {
connection = JDBCUtilsByDruid.getConnection();
return qr.query(connection,sql,new BeanHandler(tClass),parameters);
} catch (SQLException e) {
throw new RuntimeException(e); //将编译异常 -> 运行异常,抛出
}finally {
JDBCUtilsByDruid.close(null,null,connection);
}
}
//查询单行单列的方法,即返回单值的方法
public Object queryScalar(String sql,Object... parameters){
Connection connection = null;
try {
connection = JDBCUtilsByDruid.getConnection();
return qr.query(connection, sql, new ScalarHandler(), parameters);
} catch (SQLException e) {
throw new RuntimeException(e); //将编译异常 -> 运行异常,抛出
}finally {
JDBCUtilsByDruid.close(null,null,connection);
}
}
}
public class ActorDAO extends BasicDAO{
//1. 继承了BasicDAO,有了BasicDAO的方法
//2. 根据业务需求,可以编写特有的方法
}
Test.dao:
public class TestDAO {
//测试ActorDAO 对 actor表的crud操作
@Test
public void testActorDAO(){
ActorDAO actorDAO = new ActorDAO();
//1. 查询多行记录
List actors = actorDAO.queryMulti("select * from actor where id >= ?", Actor.class, 4);
for (Actor actor : actors) {
System.out.println(actor);
}
//2. 查询单行记录
Actor actor = actorDAO.querySingle("select * from actor where id = ?", Actor.class, 4);
System.out.println(actor);
//3. 查询单行单列
Object o = actorDAO.queryScalar("select * from actor where id = ?", 4);
System.out.println(o);
//4. dml 操作
int update = actorDAO.update("insert into actor values(null,?,?,?,?)", "张无忌", "男", "2000-11-11", 900);
System.out.println(update > 0? "执行成功" : "执行没有影响表");
}
}
public class Actor {
private Integer id;
private String name;
private String sex;
private Date borndate; //用Util包下的date
private Integer phone;
public Actor() {//一定要给一个无参构造器,反射需要
}
public Actor(Integer id, String name, String sex, Date borndate, Integer phone) {
this.id = id;
this.name = name;
this.sex = sex;
this.borndate = borndate;
this.phone = phone;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Date getBorndate() {
return borndate;
}
public void setBorndate(Date borndate) {
this.borndate = borndate;
}
public Integer getPhone() {
return phone;
}
public void setPhone(Integer phone) {
this.phone = phone;
}
@Override
public String toString() {
return "Actor{" +
" + id +
", name='" + name + ''' +
", sex='" + sex + ''' +
", borndate=" + borndate +
", phone=" + phone +
'}';
}
}
dao.dao:
public class BasicDAO {//泛型指定具体的类型
private QueryRunner qr = new QueryRunner();
//开发通用的DML方法,针对任意的表
//Object... parameters给sql语句中的?补充相应的值
public int update(String sql,Object... parameters){
Connection connection = null;
try {
connection = JDBCUtilsByDruid.getConnection();
int update = qr.update(connection, sql, parameters);
return update;
} catch (SQLException e) {
throw new RuntimeException(e); //将编译异常 -> 运行异常,抛出
}finally {
JDBCUtilsByDruid.close(null,null,connection);
}
}
//返回多个对象(及查询的结果是多行),针对任意表
public List queryMulti(String sql,Class tClass, Object... parameters){
Connection connection = null;
try {
connection = JDBCUtilsByDruid.getConnection();
return qr.query(connection, sql, new BeanListHandler(tClass), parameters);
} catch (SQLException e) {
throw new RuntimeException(e); //将编译异常 -> 运行异常,抛出
}finally {
JDBCUtilsByDruid.close(null,null,connection);
}
}
//查询单行结果的通用方法
public T querySingle(String sql,Class tClass,Object... parameters){
Connection connection = null;
try {
connection = JDBCUtilsByDruid.getConnection();
return qr.query(connection,sql,new BeanHandler(tClass),parameters);
} catch (SQLException e) {
throw new RuntimeException(e); //将编译异常 -> 运行异常,抛出
}finally {
JDBCUtilsByDruid.close(null,null,connection);
}
}
//查询单行单列的方法,即返回单值的方法
public Object queryScalar(String sql,Object... parameters){
Connection connection = null;
try {
connection = JDBCUtilsByDruid.getConnection();
return qr.query(connection, sql, new ScalarHandler(), parameters);
} catch (SQLException e) {
throw new RuntimeException(e); //将编译异常 -> 运行异常,抛出
}finally {
JDBCUtilsByDruid.close(null,null,connection);
}
}
}
public class ActorDAO extends BasicDAO{
//1. 继承了BasicDAO,有了BasicDAO的方法
//2. 根据业务需求,可以编写特有的方法
}
Test.dao:
public class TestDAO {
//测试ActorDAO 对 actor表的crud操作
@Test
public void testActorDAO(){
ActorDAO actorDAO = new ActorDAO();
//1. 查询多行记录
List actors = actorDAO.queryMulti("select * from actor where id >= ?", Actor.class, 4);
for (Actor actor : actors) {
System.out.println(actor);
}
//2. 查询单行记录
Actor actor = actorDAO.querySingle("select * from actor where id = ?", Actor.class, 4);
System.out.println(actor);
//3. 查询单行单列
Object o = actorDAO.queryScalar("select * from actor where id = ?", 4);
System.out.println(o);
//4. dml 操作
int update = actorDAO.update("insert into actor values(null,?,?,?,?)", "张无忌", "男", "2000-11-11", 900);
System.out.println(update > 0? "执行成功" : "执行没有影响表");
}
}
public class TestDAO {
//测试ActorDAO 对 actor表的crud操作
@Test
public void testActorDAO(){
ActorDAO actorDAO = new ActorDAO();
//1. 查询多行记录
List actors = actorDAO.queryMulti("select * from actor where id >= ?", Actor.class, 4);
for (Actor actor : actors) {
System.out.println(actor);
}
//2. 查询单行记录
Actor actor = actorDAO.querySingle("select * from actor where id = ?", Actor.class, 4);
System.out.println(actor);
//3. 查询单行单列
Object o = actorDAO.queryScalar("select * from actor where id = ?", 4);
System.out.println(o);
//4. dml 操作
int update = actorDAO.update("insert into actor values(null,?,?,?,?)", "张无忌", "男", "2000-11-11", 900);
System.out.println(update > 0? "执行成功" : "执行没有影响表");
}
}



