JDBC概述,主要API,CRUD操作,封装jdbcutil工具类,注入攻击问题, Statement与PreparedStatement的区别,数据库连接池,事物操作,批处理

本文详细介绍了Java使用JDBC进行数据库操作的流程,包括加载驱动、建立连接、执行SQL以及关闭资源。同时,文章讨论了Statement与PreparedStatement的区别,强调了PreparedStatement在防止SQL注入和提高效率方面的优势。此外,还介绍了数据库连接池的概念,以c3p0为例展示了配置和使用方法,以及如何在批处理操作中利用连接池进行性能优化。
摘要由CSDN通过智能技术生成

JDBC概述

理解:web应用 html语法 ---- java语法 ----- sql语法

任何持久层数据库连接的框架都是以jdbc为基础。(mybatis、hibernate、data-jpa、自定义持久层框架)

JDBC是Java Database Connectivity的缩写,意为Java数据库连接。是Java提供的一种数据库访问规则、规范, 由于数据库种类较多,并且java语言使用比较广泛,sun公司就提供了一种规范,让其他的数据库提供商去实现底层的访问规则。 Java程序只要使用sun公司提供的标准接口去操作数据库即可。

JDBC主要API

  • DriverManager (管理驱动:注册驱动、获取连接对象)
  • Connection (代表到数据库的连接,获取执行对象)
  • Statement、PreparedStatement (执行对象,可以执行CRUD语句)
  • ResultSet (查询结果集对象,封装了查询结果)

JDBC CRUD操作

JDBC操作数据库一般流程

我们在使用JDBC操作数据库时的流程是固定的:

  1. 加载驱动类(只需加载一次,新版本JDK可以省略)
  2. 建立连接Connection
  3. 获取执行语句对象StatementPreparedStatement
  4. 执行sql
  5. 封装结果集
  6. 释放资源、关闭连接

查询示例:

package com.lanou.jdbc;

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

public class Jdbc01 {
	/*
	 * 通过jdbc连接mysql,查询学生表所有数据,在控制台打印输出
	 */
	public static void main(String[] args) throws ClassNotFoundException, SQLException {
		//jdbc操作基本步骤
		//1、加载驱动类
		Class.forName("com.mysql.jdbc.Driver");
		//2、建立连接  建立和mysql的连接
		//jdbc:mysql://ip地址或主机名:端口号/库名?useSSL=false
		String url = "jdbc:mysql://localhost:3306/studentmanage?useSSL=false";
		Connection conn = DriverManager.getConnection(url, 
				"root", "123456");
		//3、创建一个Statement语句
		Statement stmt = conn.createStatement();
		//4、执行sql  获取结果集
		String sql = "select * from student";
		ResultSet rs = stmt.executeQuery(sql);
		//5、封装结果集(输出数据)
		while(rs.next()) { 
			//获取当前行数据
			int sid = rs.getInt("sid");
			String sname = rs.getString("sname");
			String ssex = rs.getString("ssex");
			int sage = rs.getInt("sage");
			System.out.println("学号:"+sid+ "  姓名:"+sname+"  性别:"+ssex+"  年龄:"+sage);
		}
		//6、关闭资源、释放连接  先开的后关
		rs.close();
		stmt.close();
		conn.close();
	}
}

增删改:

package com.lanou.jdbc;

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

public class Jdbc02 {
	/**
	 * 新增学生
	 * 修改学生信息  将鑫鑫的年龄加减2岁
	 * 删除学生        通过主键id列删除数据
	 * @param args
	 * @throws ClassNotFoundException 
	 * @throws SQLException 
	 */
	public static void main(String[] args) throws ClassNotFoundException, SQLException {
		//1、加载驱动
		Class.forName("com.mysql.jdbc.Driver");
		//2、建立连接
		String url = "jdbc:mysql://localhost:3306/studentmanage?useSSL=false";
		String user = "root";
		String password = "123456";
		Connection conn = DriverManager.getConnection(url, user, password);
		//3、获取Statement语句
		Statement stmt = conn.createStatement();
		//4、执行sql
		String sql = "insert into student(sname,sage,ssex) values ('鑫鑫',20,'男')";
		//执行任何sql,但是返回的是受影响的数据行数
		int num = stmt.executeUpdate(sql);
		//5、处理结果集
		System.out.println("新增成功:"+num);
		//6、关闭结果集
		stmt.close();
		conn.close();
	}
}

封装jdbcUtil工具类

package com.lanou.util;

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

/*
 * 打开连接
 * 关闭连接
 */
public class JdbcUtil {
	private static final String url = "jdbc:mysql://localhost:3306/studentmanage?useSSL=false";
	private static final String username = "root";
	private static final String password = "123456";
	private static Connection conn = null;
	static {
		//1、加载驱动类
		try {
			Class.forName("com.mysql.jdbc.Driver");
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public static Connection open() {
		try {
			conn = DriverManager.getConnection(url,username,password);
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return conn;
	}
	
	public static void close(ResultSet rs,Statement stmt,Connection conn) {
		//6、关闭资源、释放连接  先开的后关
		if(rs != null) {
			try {
				rs.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		if(stmt != null) {
			try {
				stmt.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		if(conn != null) {
			try {
				conn.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	
}

使用jdbcUtil工具类完成查询

public void query(){
		Connection conn = null;
		Statement stmt =  null;
		ResultSet rs = null;
		try {
			conn = JdbcUtil.open();
			//3、创建一个Statement语句
			stmt = conn.createStatement();
			//4、执行sql  获取结果集
			String sql = "select * from student";
			rs = stmt.executeQuery(sql);
			//5、封装结果集(输出数据)
			while(rs.next()) { 
				//获取当前行数据
				int sid = rs.getInt("sid");
				String sname = rs.getString("sname");
				String ssex = rs.getString("ssex");
				int sage = rs.getInt("sage");
				System.out.println("学号:"+sid+ "  姓名:"+sname+"  性别:"+ssex+"  年龄:"+sage);
			}
		}catch(Exception e) {
			e.printStackTrace();
		}finally {
			JdbcUtil.close(rs, stmt, conn);
		}
	}

注入攻击问题?

//根据姓名查询
	public void query(String name){
		Connection conn = null;
		Statement stmt =  null;
		ResultSet rs = null;
		try {
			conn = JdbcUtil.open();
			//3、创建一个Statement语句
			stmt = conn.createStatement();
			//4、执行sql  获取结果集
			String sql = "select * from student where sname='"+name+"' ";
			rs = stmt.executeQuery(sql);
			//5、封装结果集(输出数据)
			while(rs.next()) { 
				//获取当前行数据
				int sid = rs.getInt("sid");
				String sname = rs.getString("sname");
				String ssex = rs.getString("ssex");
				int sage = rs.getInt("sage");
				System.out.println("学号:"+sid+ "  姓名:"+sname+"  性别:"+ssex+"  年龄:"+sage);
			}
		}catch(Exception e) {
			e.printStackTrace();
		}finally {
			JdbcUtil.close(rs, stmt, conn);
		}
	}
	
public static void main(String[] args){
		//sql注入攻击
		new Jdbc04().query("鑫鑫' or '1' = '1 ");
}
总结:

Statement语句弊端:

​ 1、拼接sql非常麻烦导致开发效率低

​ 2、可能存在注入攻击问题,倒是数据不安全

如何解决?

PreparedStatment 代替 Statement

PreparedStatment

优点:无需拼接sql,开发效率高

​ 预编译过程,执行效率高

​ 可以避免注入攻击,安全性能更好

Statement与PreparedStatement的区别

Statement接口用来执行一段SQL语句并返回结果,不支持参数占位符写法。Statement执行 ,其实是拼接sql语句的。 先拼接sql语句,然后在一起执行。如果传入的参数是一段可执行的SQL,也会被执行,有SQL注入的风险。

PreparedStatement接口继承自Statement接口,相比较以前的statement, 预先处理给定的sql语句,对其执行语法检查。 在sql语句里面使用 ? 占位符来替代后续要传递进来的变量。 后面进来的变量值,只会被看成参数值,不会产生任何的关键字的效果。

实践操作:

能够使用PreparedStament完成常见的增上改查:

package com.lanou.dao;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

import com.lanou.bean.Student;
import com.lanou.util.JdbcUtil;
/*
 * 学生常见操作
 * 面试:
 * 1、jdbc操作基本流程
 * 2、Statement和PreparedStatement的区别和练习
 * 3、常见增删改查
 */
public class StudentDao {
	//新增一个学生对象
	public int save(Student student) {
		Connection conn = null;
		PreparedStatement stmt = null;
		int num = 0;
		try {
			conn = JdbcUtil.open();
			//? 问号占位符
			String sql = "insert into student(sname,ssex,sage) values (?,?,?)";
			//预编译sql
			stmt = conn.prepareStatement(sql);
			//给 ?号占位符赋值
			stmt.setString(1, student.getSname());
			stmt.setString(2, student.getSsex());
			stmt.setInt(3, student.getSage());
			//执行sql
			num = stmt.executeUpdate();
			
		}catch(Exception e) {
			e.printStackTrace();
		}finally {
			JdbcUtil.close(null, stmt, conn);
		}
		return num;
	}
	//查询所有学生对象
	public List<Student> findAll(){
		Connection conn = null;
		PreparedStatement stmt = null;
		ResultSet rs = null;
		List<Student> list = new ArrayList<>();
		try {
			conn = JdbcUtil.open();
			String sql = "select * from student";
			stmt = conn.prepareStatement(sql);
			//执行查询
			rs = stmt.executeQuery();
			//遍历
			while(rs.next()) {	
				int sid = rs.getInt("sid");
				int sage = rs.getInt("sage");
				String sname = rs.getString("sname");
				String ssex = rs.getString("ssex");
				//每循环一次,创建一个对象
				Student s = new Student(sid,sname,ssex,sage);
				list.add(s);
			}
			
		}catch(Exception e) {
			e.printStackTrace();
		}finally {
			JdbcUtil.close(rs, stmt, conn);
		}
		return list;
	}
	
	//通过主键查询一个学生对象
	public Student findOne(int sid){
		Connection conn = null;
		PreparedStatement stmt = null;
		Student student = null;
		ResultSet rs = null;
		try {
			conn = JdbcUtil.open();
			String sql = "select * from student where sid = ?";
			stmt = conn.prepareStatement(sql);
			//赋值
			stmt.setInt(1, sid);
			//执行查询
			rs = stmt.executeQuery();
			//遍历
			if(rs.next()) {
				//每循环一次,创建一个对象
				int sid1 = rs.getInt("sid");
				int sage = rs.getInt("sage");
				String sname = rs.getString("sname");
				String ssex = rs.getString("ssex");
				student = new Student(sid1,sname,ssex,sage);
			}
			
			
			
		}catch(Exception e) {
			e.printStackTrace();
		}finally {
			JdbcUtil.close(null, stmt, conn);
		}
		return student;
	}
	
	//修改学生对象
	public int update(Student student) {
		Connection conn = null;
		PreparedStatement stmt = null;
		int num = 0;
		try {
			conn = JdbcUtil.open();
			String sql = "update student set sname=? , ssex=? ,sage=? where sid = ?";
			stmt = conn.prepareStatement(sql);
			//赋值
			stmt.setString(1, student.getSname());
			stmt.setString(2, student.getSsex());
			stmt.setInt(3, student.getSage());
			stmt.setInt(4, student.getSid());
			//执行sql
			num = stmt.executeUpdate();
			
		}catch(Exception e) {
			e.printStackTrace();
		}finally {
			JdbcUtil.close(null, stmt, conn);
		}
		return num;
	}
	
	//删除学生对象 通过主键删除
	public int delete(int sid) {
		Connection conn = null;
		PreparedStatement stmt = null;
		int num = 0;
		try {
			conn = JdbcUtil.open();
			String sql = "delete from student where sid = ?";
			stmt = conn.prepareStatement(sql);
			//赋值
			stmt.setInt(1, sid);
			//执行
			num = stmt.executeUpdate();
			
			
		}catch(Exception e) {
			e.printStackTrace();
		}finally {
			JdbcUtil.close(null, stmt, conn);
		}
		return num;
	}
}

测试类:

public static void main(String[] args) {
		Student s1 = new Student("zhangsan", "女", 19);
		StudentDao dao = new StudentDao();	
//		int num = dao.save(s1);
//		System.out.println("新增:"+num);
		
		
//		int num2 = dao.delete(7);
//		System.out.println("删除:"+num2);
		
		
//		Student s2 = new Student(6, "zhangsan", "女", 19);
//		int num3 = dao.update(s2);
//		System.out.println("更新:"+num3);
		
		List<Student> list = dao.findAll();
		System.out.println(list.toString());
		
		Student s3 = dao.findOne(9);
		System.out.println(s3 == null ? "该对象不存在" :s3.toString());
		
	}

Student的javaBean:

package com.lanou.bean;
/*
 * 类---表
 * 属性---字段
 * 属性类型---字段类型
 * int         int
 * String      char/varchar
 * Date        date/datetime
 * TimeStamp   timestamp
 */
public class Student {
	private int sid;
	private String sname;
	private String ssex;
	private int sage;
	public int getSid() {
		return sid;
	}
	public void setSid(int sid) {
		this.sid = sid;
	}
	public String getSname() {
		return sname;
	}
	public void setSname(String sname) {
		this.sname = sname;
	}
	public String getSsex() {
		return ssex;
	}
	public void setSsex(String ssex) {
		this.ssex = ssex;
	}
	public int getSage() {
		return sage;
	}
	public void setSage(int sage) {
		this.sage = sage;
	}
	public Student(int sid, String sname, String ssex, int sage) {
		super();
		this.sid = sid;
		this.sname = sname;
		this.ssex = ssex;
		this.sage = sage;
	}
	public Student() {
		super();
	}
	public Student(String sname, String ssex, int sage) {
		super();
		this.sname = sname;
		this.ssex = ssex;
		this.sage = sage;
	}
	@Override
	public String toString() {
		return "Student [sid=" + sid + ", sname=" + sname + ", ssex=" + ssex + ", sage=" + sage + "]";
	}
	
	
}

jdbc第三天

1、周报 解决周报问题

作业量稍大:必选+附加

技术点:

​ 1)什么是javabean

​ java语言写的一个可重用的组件,成为javaBean。

​ 写javaBean满足:公共的方法,对外部可见,让外部调用。具有无参构造方法。

​ 一般来讲,类中有私有属性、提供私有属性的getter、setter方法、提供构造方法。

​ 2)Statement和PreparedStatement

​ Statement : PreparedStatement ===== 父:子

​ select id,name from 表名;

​ 一般来说,没有特殊要求时,都统一使用PreparedStatement,效率高、不用拼接sql,避免注入攻击。

​ 如果表名、库名 ,列名需要动态传入时,不能使用预编译,只能采用sql拼接。

​ 注入攻击

​ 如何防止注入攻击

/*
 * 对比Statement和PreparedStatement
 * 1、Statement拼接sql  
 * 		如果表名、列名、库名需要动态传入,必须选择拼接
 * 2、PreparedStatement 如何防止注入攻击?
 * 		 'zhangsan\' or \'1\'=\'1'  对传入的字符串中特殊字符,转义为普通字符
 */
 
 //Statement 可以 拼接  表名、列名
	public List<Student> listByName1(String tablename){
		Connection conn = null;
		Statement stmt = null;
		ResultSet rs = null;
		List<Student> list = new ArrayList<Student>();
		try {
			conn = JdbcUtil.open();
			stmt = conn.createStatement();
			rs = stmt.executeQuery("select * from "+tablename);
			while(rs.next()) {
				int sid = rs.getInt("sid");//列名 常用
				String sname = rs.getString(2);//列索引 从1开始排  
				int sage = rs.getInt(3);//如果一个表中有100个列,索引没法数
				String ssex = rs.getString(4);
				Student stu = new Student(sid, sname, ssex, sage);
				list.add(stu);
			}
		}catch(Exception e) {
			e.printStackTrace();
		}finally {
			JdbcUtil.close(rs, stmt, conn);
		}
		return list;
	}
	
	//PreparedStatement
	public List<Student> listByName2(String tablename,String column){
		Connection conn = null;
		PreparedStatement stmt = null;
		ResultSet rs = null;
		List<Student> list = new ArrayList<Student>();
		try {
			conn = JdbcUtil.open();
			stmt = conn.prepareStatement("select ? from student");
			stmt.setObject(1, column);
			rs = stmt.executeQuery();
			while(rs.next()) {
				int sid = rs.getInt("sid");//列名 常用
				String sname = rs.getString(2);//列索引 从1开始排  
				int sage = rs.getInt(3);//如果一个表中有100个列,索引没法数
				String ssex = rs.getString(4);
				Student stu = new Student(sid, sname, ssex, sage);
				list.add(stu);
			}
		}catch(Exception e) {
			e.printStackTrace();
		}finally {
			JdbcUtil.close(rs, stmt, conn);
		}
		return list;
	}
	/*
	 * 注入攻击
	 */
	public List<Student> listByName3(String name){
		Connection conn = null;
		Statement stmt = null;
		ResultSet rs = null;
		List<Student> list = new ArrayList<Student>();
		try {
			conn = JdbcUtil.open();
			stmt = conn.createStatement();
			String sql = "select * from student where sname = '"+name+"'";
			rs = stmt.executeQuery(sql);
			while(rs.next()) {
				int sid = rs.getInt("sid");//列名 常用
				String sname = rs.getString(2);//列索引 从1开始排  
				int sage = rs.getInt(3);//如果一个表中有100个列,索引没法数
				String ssex = rs.getString(4);
				Student stu = new Student(sid, sname, ssex, sage);
				list.add(stu);
			}
		}catch(Exception e) {
			e.printStackTrace();
		}finally {
			JdbcUtil.close(rs, stmt, conn);
		}
		return list;
	}
	
	/*
	 * 使用预编译 防止注入攻击
	 * 执行sql api
	 * 相同点:三种方法都可以执行sql
	 * 不同点:返回结果集不同
	 * 
	 * 	1、executeUpdate() int 返回的是受影响的数据行  增、删、改
	 *  2、executeQuery()  ResultSet 返回的是查询的结果集  
	 *  3、execute()  	   boolean  
	 *  	查询:	true if the first result is a ResultSetobject; 
	 *  	增删改:	false if the first result is an updatecount or there is no result
	 */
	public List<Student> listByName4(String name){
		Connection conn = null;
		PreparedStatement stmt = null;
		ResultSet rs = null;
		List<Student> list = new ArrayList<Student>();
		try {
			conn = JdbcUtil.open();
			String sql = "select * from student where sname = ?";
			stmt = conn.prepareStatement(sql);
			stmt.setString(1, name);
			rs = stmt.executeQuery();
			while(rs.next()) {
				int sid = rs.getInt("sid");//列名 常用
				String sname = rs.getString(2);//列索引 从1开始排  
				int sage = rs.getInt(3);//如果一个表中有100个列,索引没法数
				String ssex = rs.getString(4);
				Student stu = new Student(sid, sname, ssex, sage);
				list.add(stu);
			}
		}catch(Exception e) {
			e.printStackTrace();
		}finally {
			JdbcUtil.close(rs, stmt, conn);
		}
		return list;
	}
	
	

​ 3)约束不熟练

给列添加约束,限制数据的输入,保证数据的完整性和一致性。
primary key   非空+唯一
forign key    参照其他表数据  一致性
unique        唯一  可以有一个null
not null      非空,不能为空值
default       设置默认值

​ 4)sql 思路 多表联合查询

-- 统计学生学号、姓名和平均成绩
-- 列 和 表
	 student ,sc 
-- 每个学生平均成绩 根据学号分组,得平均成绩
		sid avg(score)
		1  90
		2  80

select sid ,avg(score) 
from sc
group by sid;

select sname,sc2.* from student,(select sid ,avg(score) 
from sc
group by sid) sc2
where student.sid = sc2.sid

select student.sid ,sname,avg(score) 
from sc join student
on sc.sid = student.sid
group by sid;

​ 5)子查询

-- 参加考试的学号和姓名
select distinct sid from sc;
select sid,sname from student where sid in (1,2,3);

select sid,sname from student where sid in (select distinct sid from sc);

-- 没有参加考试的学号和姓名

select sid,sname from student where sid not in (select distinct sid from sc);

​ 6)左右外连接

内连接:
	查找连个表匹配的数据,能够关联上的数据。
外连接:
	student  sid  1,2,3,4
	score    sid  1,2,3
左外:from  A  left outer join  B
右外:from  A  right outer join B

​ 7)索引的使用

不需要调用。

sql优化器自动调用。

-- 全表扫描   从第一条到最后一条依次匹配   数据量小效率高 10-100-1000  0.01s
	                                 表中数据 10000000000  10s  效率低
-- 索引查找   hash、tree  快速定位   数据量大、重复数据少 

​ 8)jdbc主要api

* 相同点:三种方法都可以执行sql
 * 不同点:返回结果集不同
 * 
 * 	1、executeUpdate() int 返回的是受影响的数据行  增、删、改
 *  2、executeQuery()  ResultSet 返回的是查询的结果集  
 *  3、execute()  	   boolean  
 *  	查询:	true if the first result is a ResultSetobject; 
 *  	增删改:	false if the first result is an updatecount or there is no result
 */
while(rs.next()) {
				int sid = rs.getInt("sid");//列名 常用
				String sname = rs.getString(2);//列索引 从1开始排  
				int sage = rs.getInt(3);//如果一个表中有100个列,索引没法数
				String ssex = rs.getString(4);
				Student stu = new Student(sid, sname, ssex, sage);
				list.add(stu);
			}

2、抽查作业

3、jdbc回顾

4、jdbc连接池

数据库连接池

数据库连接池介绍

数据库连接池技术与我们前面学的线程池技术有点类似。都是"池化"技术,核心思想都是通过缓存一部分资源,从而达到复用的目的,可以一定程度上避免每次使用都重复创建、销毁的过程,节省系统开销,提高程序性能。

目前市面上主流的数据库连接池技术有:

  • DBCP
  • c3p0
  • Druid

c3p0连接池用法

本章以c3p0连接池技术为例讲解一下连接池的用法,其他连接池与此类似,可参考各自官方文档、示例代码。

  1. 添加连接池依赖包

    将以下两个jar包添加到工程的构建路径中

    • c3p0-0.9.5.4.jar
    • mchange-commons-java-0.2.16.jar
  2. 编写c3p0配置文件

    c3p0配置文件中允许我们配置一个默认数据源和多个命名数据源,可以在程序中指定加载哪个

    在工程src根目录新建c3p0-config.xml文件,在其中填入以下内容

    <?xml version="1.0" encoding="UTF-8"?>
    <c3p0-config>
    	<default-config>
        	<property name="driverClass">com.mysql.jdbc.Driver</property>
        	<property name="jdbcUrl">jdbc:mysql://localhost:3306/yanfa3</property>
    		<property name="user">root</property>
    		<property name="password">root</property>
    		<property name="initialPoolSize">40</property>		
    		<property name="minPoolSize">10</property>
    		<property name="maxPoolSize">60</property>
    		<property name="maxStatements">100</property>
    		<property name="maxIdleTime">60</property>
    	</default-config>
    	<named-config name="mysql">
    		<property name="driverClass">com.mysql.jdbc.Driver</property>
        	<property name="jdbcUrl">jdbc:mysql://localhost:3306/yanfa3</property>
    		<property name="user">root</property>
    		<property name="password">root</property>
    		<property name="initialPoolSize">40</property>		
    		<property name="minPoolSize">10</property>
    		<property name="maxPoolSize">60</property>
    		<property name="maxStatements">100</property>
    		<property name="maxIdleTime">60</property>
    	</named-config>
    	<named-config name="oracle">
    		<property name="driverClass">oracle.jdbc.OracleDriver</property>
        	<property name="jdbcUrl">jdbc:oracle:thin:@//localhost:1521:SERVICENAME</property>
    		<property name="user">root</property>
    		<property name="password">root</property>
    		<property name="initialPoolSize">40</property>		
    		<property name="minPoolSize">10</property>
    		<property name="maxPoolSize">60</property>
    		<property name="maxStatements">100</property>
    		<property name="maxIdleTime">60</property>
    	</named-config>
    	<named-config name="sqlserver">
    		<property name="driverClass">com.microsoft.sqlserver.jdbc.SQLServerDriver</property>
        	<property name="jdbcUrl">jdbc:sqlserver://localhost:1433;DatabaseName=yanfa3</property>
    		<property name="user">root</property>
    		<property name="password">root</property>
    		<property name="initialPoolSize">40</property>		
    		<property name="minPoolSize">10</property>
    		<property name="maxPoolSize">60</property>
    		<property name="maxStatements">100</property>
    		<property name="maxIdleTime">60</property>
    	</named-config>
    </c3p0-config>
    

    可以将此文件保存一份,用的时候修改里面的参数

  3. 通过c3p0连接池获取连接

    // 读取配置文件中的默认配置,获取数据源
    DataSource ds = new ComboPooledDataSource();
    
    
    // 读取配置文件中指定配置,获取数据源
    DataSource ds = new ComboPooledDataSource("mysql");
    
    // 通过数据源对象获取单个连接
    Connection conn = ds.getConnection();
    

jdbcUtil工具类:

package com.lanou.util;

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

import javax.sql.DataSource;

import com.mchange.v2.c3p0.ComboPooledDataSource;

/*
 * 打开连接
 * 关闭连接
 */
public class JdbcUtil {
//	private static final String url = "jdbc:mysql://localhost:3306/studentmanage?useSSL=false";
//	private static final String username = "root";
//	private static final String password = "123456";
	private static Connection conn = null;
	private static DataSource ds = null;
	static {
		//1、加载驱动类
//		try {
//			Class.forName("com.mysql.jdbc.Driver");
//		} catch (ClassNotFoundException e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		}
		ds = new ComboPooledDataSource();
	}
	
	public static Connection open() {
		try {
//			conn = DriverManager.getConnection(url,username,password);
			conn = ds.getConnection();
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return conn;
	}
	
	public static void close(ResultSet rs,Statement stmt,Connection conn) {
		//6、关闭资源、释放连接  先开的后关
		if(rs != null) {
			try {
				rs.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		if(stmt != null) {
			try {
				stmt.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		if(conn != null) {
			try {
				conn.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	
}

5、补充内容
返回自增主键(前提是insert语句)

​ executeUpdate(sql—insert 、update、delete)

​ insert新增数据时,没有指定主键,新增成功后,返回当前新增数据行的主键。

​ update更新数据,一般是通过主键字段,更新其他字段。

(任意表student,新增姓名、性别,返回当前sid)

/*
	 * 返回自增主键
	 */
	public int save(Student student) {
		Connection conn = null;
		PreparedStatement stmt = null;
		ResultSet rs = null;
		int num = 0;
		try {
			conn = JdbcUtil.open();
			String sql = "insert into student(sname,ssex) values (?,?)";
			//返回主键值
			stmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
			stmt.setString(1, student.getSname());
			stmt.setString(2, student.getSsex());
			//执行sql
			int num1 = stmt.executeUpdate();//新增、更新数据行数
			//返回主键 
			rs = stmt.getGeneratedKeys();
			//获取结果集 一行一列
			if(rs.next()) {
				num = rs.getInt(1);//获取第一列的值  1索引位置为第一列
			}
			 
		}catch(Exception e) {
			e.printStackTrace();
		}finally {
			JdbcUtil.close(rs, stmt, conn);
		}
		return num;
	}
写c3p0连接池模式下完成增删改查时常见错误:

1、引入jar包 3个

​ mysql.jar 、mchange、c3p0

2、连接池配置文件 目前只能放在 src目录下,不能在src子目录中,而且文件名必须是c3p0-config.xml

3、c3p0-config.xml 配置文件内容


在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

事务

​ 四个特性:acid 原子性、一致性、隔离性、持久性

​ 使用步骤:

​ 1、设置自动提交模式关闭

​ 2、开始事务

​ 3、执行dml操作(insert、update、delete)

​ 4、提交或者回滚事务

​ 5、设置到默认自动提交模式

public  void   doWork(){
     取消事务的自动提交机制,设置事务为手动提交.
     Connection对象.setAutoCommit(false);
     try{
        操作1:  保存数据到订单表  
        操作2:  保存产品数据到订单详情表
        -------------停电--------------
        操作3:  更新库存
        提交事务
        Connection对象.commit();
     }catch(Exception e){
         回滚事务,取消之前所有的操作 
         Connection对象.rollback();
     }finally{
        释放资源
     }
}

​ 示例:

/*
	 * 先新增、再更新
	 */
	public int saveAndUpdate(Student student) {
		Connection conn = null;
		PreparedStatement stmt = null;
		ResultSet rs = null;
		int num = 0;
		try {
			//获取连接
			conn = JdbcUtil.open();
			//设置自动提交模式关闭
			conn.setAutoCommit(false);
			String sql = "insert into student(sname,ssex) values (?,?)";
			//返回主键值
			stmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
			stmt.setString(1, student.getSname());
			stmt.setString(2, student.getSsex());
			//执行sql
			int num1 = stmt.executeUpdate();//新增、更新数据行数
			//返回主键 
			rs = stmt.getGeneratedKeys();
			//获取结果集 一行一列
			if(rs.next()) {
				num = rs.getInt(1);
			}
			//新增成功后,更新当前主键对应的age
			String sql2 = "update student set sage = ? where sid = ?";
			stmt = conn.prepareStatement(sql2);
			stmt.setInt(1, student.getSage());
			stmt.setInt(3, num);//3是为了模拟失败情况
			stmt.executeUpdate();
			
			//提交事务
			conn.commit();
			 
		}catch(Exception e) {
			try {
				//回滚事务
				conn.rollback();
			} catch (SQLException e1) {
				e1.printStackTrace();
			}
			e.printStackTrace();
		}finally {
			JdbcUtil.close(rs, stmt, conn);
		}
		return num;
	}
批处理

​ 批量操作(新增)

​ 前端 List 全部新增到数据库表中。

​ 录入30个用户,提交到数据库中。

​ 每次提交一个 录入一个、按钮提交。

​ 一次性录入30行,按钮同时保存。

	//测试,模拟数据
	public void test(){
		//模拟30个用户
		List<Student> list = new ArrayList();
		for(int i=0;i<30;i++) {
			Student student = new Student("wang"+i, "男", 15+i);
			list.add(student);
		}
		long startTime = System.currentTimeMillis();
		dao.saveBatch2(list);
		System.out.println(System.currentTimeMillis()-startTime);
	}
	
	/**
	 * 普通循环新增30行
	 * @param list
	 */
	public void saveBatch(List<Student> list) {
		Connection conn = null;
		PreparedStatement stmt = null;
		ResultSet rs = null;
		try {
			//获取连接
			conn = JdbcUtil.open();
			String sql = "insert into student(sname,ssex,sage) values (?,?,?)";
			stmt = conn.prepareStatement(sql);
			for(int i = 0;i<list.size();i++) {
				Student s = list.get(i);
				stmt.setString(1, s.getSname());
				stmt.setString(2, s.getSsex());
				stmt.setInt(3, s.getSage());
				stmt.executeUpdate();
			}
			
		}catch(Exception e) {
			e.printStackTrace();
		}finally {
			JdbcUtil.close(rs, stmt, conn);
		}
	}
	
	/**
	 * 批处理 1千万行
	 * 避免1行1提交对数据库服务器造成过大的压力
	 * 可以选择批量提交 比如:500行提交一次
	 */
	public void saveBatch2(List<Student> list) {
		Connection conn = null;
		PreparedStatement stmt = null;
		ResultSet rs = null;
		try {
			//获取连接
			conn = JdbcUtil.open();
			String sql = "insert into student(sname,ssex,sage) values (?,?,?)";
			stmt = conn.prepareStatement(sql);
			for(int i = 0;i<list.size();i++) {
				Student s = list.get(i);
				stmt.setString(1, s.getSname());
				stmt.setString(2, s.getSsex());
				stmt.setInt(3, s.getSage());
				stmt.addBatch();//添加当前语句到批处理操作中
				if(i%10==0) {
					stmt.executeBatch();//执行批处理操作
					//清除问号占位符
					stmt.clearParameters();
					stmt.clearBatch();
				}
			}
			stmt.executeBatch();//执行批处理操作
			
		}catch(Exception e) {
			e.printStackTrace();
		}finally {
			JdbcUtil.close(rs, stmt, conn);
		}
	}

6、自己封装jdbc工具方法(类型与框架封装)

jdbc 分页 和 高级搜索 servlet+jdbc整合讲。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值