JDBC笔记(下)
要求AA给BB转账 AA减少100元 BB增加100元 这两个必须同时出现 要么成功余额都有变化 要么都不变
String sql1="update user_table set balance= balance -100 where user=?";
JDBCUtils.update(sql1, "AA");
//模拟异常
System.out.println(10/0);
String sql2 ="update user_table set balance= balance +100 where user=?";
JDBCUtils.update(sql2, "BB");
System.out.println("转账成功");
这时 AA的钱减少了但是BB却没有增加
什么叫数据库事务
一组逻辑操作单元,使数据从一种状态变换到另一种状态 一组逻辑操作单元,一个或多个DML操作
事务处理的原则
保证所有事物都作为一个工作单元来执行 即使出现了故障都不能改变这种执行方式。当在一个食物中执行多个操作时 要么所有事物都被提交那么这些修改就永久保存下来 要么数据库管理系统放弃所作的所有修改整个事物回滚到最初状态
为确保数据库中数据的一致性 数据的操纵应当是离散的组成的逻辑单元 当它全部完成时数据的一致性可以保持 而当这个单元中的一部分操作失败整个事物应全部视为错误,所有从起点以后的操作应全部回退到开始状态。
数据一旦提交就不可回滚
哪些操作会导致数据的自动提交
1.DDL为解释为数据定义语言,使用DDL对事务的操作是隐性提交的,不能回滚,常见操作是对数据库或者表进行创建,alter和drop操作
2.DML操作默认情况下 一旦执行就会自动提交 -------- 我们可以通过set autocommit =false 取消DML操作的自动提交insert,delete,update,select
3.默认在关闭连接时 会自动提交数据
所以考虑事物的修改(没有进行异常处理 方便看)
public static void main(String[] args) throws Exception {
//在user_table表中 AA给BB转账100
//update user_table set balance= balance -100 where user="AA"
//update user_table set balance= balance +100 where user="BB"
Connection conn = JDBCUtils.getConnection();
//取消数据的自动提交
String sql1="update user_table set balance= balance -100 where user=?";
conn.setAutoCommit(false);
update1(conn,sql1, "AA");
String sql2 ="update user_table set balance= balance +100 where user=?";
update1(conn,sql2, "BB");
System.out.println("转账成功");
conn.commit();//提交
JDBCUtils.closeResource(conn, null);//关闭连接 prepareStatement在方法里已经关了 关闭连接就相当于不能回滚了
}
public static void update1(Connection conn,String sql,Object ...args) {
//获取数据库连接
//预编译Sql语句 返沪PreparedStatement实例
PreparedStatement ps=null;
try {
conn = JDBCUtils.getConnection();
ps = conn.prepareStatement(sql);
for(int i=0 ;i<args.length;i++)
{ //数据库操作下标从一开始
ps.setObject(i+1, args[i]);
}
ps.execute();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally{
JDBCUtils.closeResource(null, ps); //不要给关了
}
}
事物的ACID属性
1.原子性 :原子性是指事物是一个不可分割的的工作单位,事物中的操作要么都发生 要么都不发生
2.一致性 : 事物必须使数据库从一个一致性状态变换到另一个一致的状态
3.隔离性: 事物的隔离性是指一个事物的执行不能被其他事物干扰 即一个事物内部的操作及使用的数据对并发的其它事物是隔离的 并发执
行的各个事物之间不能互相干扰
4.持久性:持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的 接下来的其他操作和数据库故障不应该对其有任何影响
Dao及其实现类
以操作Customers 为例
声明BaseDao 里面存放的是JAVA操作数据库的方式
CustomerDao 里面是想要操作Customers的接口
CustomerDaoImpl 里面是实现CustomerDao 接口的 具体方法
BaseDao
package Dao;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import JDBCUtil.JDBCUtils;
//封装了针对于数据表的通用操作
//抽象化不能实例化
public abstract class BaseDAO {
public static void update(Connection conn,String sql,Object ...args) {
//获取数据库连接
//预编译Sql语句 返沪PreparedStatement实例
PreparedStatement ps=null;
try {
conn = JDBCUtils.getConnection();
ps = conn.prepareStatement(sql);
for(int i=0 ;i<args.length;i++)
{ //数据库操作下标从一开始
ps.setObject(i+1, args[i]);
}
ps.execute();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally{
JDBCUtils.closeResource(null, ps);
}
}
public static <T> T getInstance( Connection conn,Class <T> clazz,String sql , Object...args){
PreparedStatement ps=null;
ResultSet rs=null;
try {
conn = JDBCUtils.getConnection();
ps = conn.prepareStatement(sql);
//填充占位符
for( int i=0 ;i<args.length;i++)
{
ps.setObject(i+1, args[i]);
}
rs = ps.executeQuery();
//获取结果集的元数据
ResultSetMetaData rsmd = rs.getMetaData();
//通过ResultSetMetaData获取结果集的列数
int columnCount = rsmd.getColumnCount();
if(rs.next())
{ T t = clazz.newInstance();
//处理结果集一行数据中的每一个列
for(int i=0;i<columnCount;i++)
{ //获取列值
Object columValue = rs.getObject(i+1);
//获取每个列的列名
String columnName = rsmd.getColumnName(i+1);
//给 c对象指定的columnName属性 赋值为columValue 通过反射的方法
//(还看不懂)
System.out.println(columnName);
Field field = clazz.getDeclaredField(columnName);
field.setAccessible(true);
field.set(t,columValue );
}
return t;
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally{
JDBCUtils.closeResource(null, ps, rs);
}
return null;
}
public static <T>List<T> getList( Connection conn,Class <T> clazz,String sql , Object...args){
PreparedStatement ps=null;
ResultSet rs=null;
try {
conn = JDBCUtils.getConnection();
ps = conn.prepareStatement(sql);
//填充占位符
for( int i=0 ;i<args.length;i++)
{
ps.setObject(i+1, args[i]);
}
rs = ps.executeQuery();
//获取结果集的元数据
ResultSetMetaData rsmd = rs.getMetaData();
//通过ResultSetMetaData获取结果集的列数
int columnCount = rsmd.getColumnCount();
ArrayList<T> list = new ArrayList<T>();
while(rs.next())
{ T t = clazz.newInstance();
//处理结果集一行数据中的每一个列
for(int i=0;i<columnCount;i++)
{ //获取列值
Object columValue = rs.getObject(i+1);
//获取每个列的列名
String columnName = rsmd.getColumnName(i+1);
//给 c对象指定的columnName属性 赋值为columValue 通过反射的方法
//(还看不懂)
Field field = clazz.getDeclaredField(columnName);
field.setAccessible(true);
field.set(t,columValue );
}
list.add(t);
}
return list;
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally{
JDBCUtils.closeResource(null, ps, rs);
}
return null;
}
//用于查询特殊值的通用方法
public<E> E getValue(Connection conn,String sql,Object...args) {
PreparedStatement ps=null;
ResultSet resultSet=null;
try {
ps = conn.prepareStatement(sql);
for(int i=0 ;i<args.length;i++)
{
ps.setObject(i+1, args[i]);
}
resultSet = ps.executeQuery();
if(resultSet.next())
{
return (E) resultSet.getObject(1);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
finally{
JDBCUtils.closeResource(null, ps,resultSet);
}
return null;
}
}
CustomersDao
package Dao;
import java.sql.Connection;
import java.sql.Date;
import java.util.List;
import com.javabean.Customers;
public interface CustomerDAO {
//此接口用语规范针对于customers常用操作
//将cust对象添加到数据库中
public void insert(Connection conn ,Customers cust);
//根据指定ID删除表中的一条记录
public void deleteById(Connection conn, int id );
//修改 针对内存中的cust对象 修改数据表中指定的记录
public void update(Connection conn ,Customers cust);
//针对指定id查询得到对应的Customer对象
public Customers CustomerById(Connection conn,int id );
//查询表中所有记录构成的集合
public List<Customers> CustomerAll(Connection conn);
//查询表中总记录数
public Long getCount(Connection conn);
//查询最大生日
public Date MaxBirth(Connection conn);
}
CustomersImpl
package Dao;
import java.sql.Connection;
import java.sql.Date;
import java.util.List;
import com.javabean.Customers;
public class CustomersDaoImpl extends BaseDAO implements CustomerDAO {
@Override
public void insert(Connection conn, Customers cust) {
String sql ="insert into customers(name,email,birth)values(?,?,?)";
update(conn, sql, cust.getName(),cust.getEmail(),cust.getDate());
}
@Override
public void deleteById(Connection conn, int id) {
String sql ="delete from customers where id =?";
update(conn, sql, id);
}
@Override
public void update(Connection conn, Customers cust) {
String sql ="update customers set name =?,email=?,birth=? where id=?";
update(conn, sql, cust.getName(),cust.getEmail(),cust.getDate(),cust.getId());
}
@Override
public Customers CustomerById(Connection conn, int id) {
String sql ="select id,name,email,birth from customers where id =?";
Customers cust = getInstance(conn, Customers.class,sql, id);
return cust;
}
@Override
public List<Customers> CustomerAll(Connection conn) {
String sql =" select id,name.email,birth from customers";
List<Customers> list = getList(conn, Customers.class, sql);
return list;
}
@Override
public Long getCount(Connection conn) {
String sql ="select count (*) from customers";
return getValue(conn, sql);
}
@Override
public Date MaxBirth(Connection conn) {
String sql ="select max(birth) from customers";
return getValue(conn, sql);
}
}
然后进行测试
数据库连接池
为解决传统开发中的数据库连接问题 可以采用数据库连接池技术。
数据库连接池基本思想:就是为了数据库连接建立一个缓冲池 预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从缓冲池中取出一个 使用完毕后再放回去
数据库连接池负责分配,管理和释放数据库连接。它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个
数据库连接池在初始化时将创建一定数量的数据库连接放到连接池中,这些数据库连接到数量是有最小数据库连接数来设定的。无论这些
数据库连接是否被使用 ,连接池都将一直保证至少拥有这么多的连接数量。连接池的最大数据库连接数量限定了这个连接池能占有的最大
连接数 当应用程序向连接池请求的连接数超过最大连接数量时 这些请求将被加入到等待队列中。
优点:
1资源重用
2.更快的系统反应速度
3.新的资源分配手段
4.统一的连接管理,避免数据库连接泄露。
1.导入jar包
C3P0
方式一
//获取C3P0数据库连接池
ComboPooledDataSource cpds = new ComboPooledDataSource();
cpds.setDriverClass("com.mysql.cj.jdbc.Driver" );
cpds.setJdbcUrl( "jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true" );
cpds.setUser("root");
cpds.setPassword("root");
//通过设置相关参数 对数据库连接池进行管理
//设置初始池子连接数
cpds.setInitialPoolSize(10);
Connection conn = cpds.getConnection();
System.out.println(conn);
DataSources.destroy(cpds); 销毁数据库连接池 一般不会使用
方式二 使用配置文件
配置文件的名( c3p0-config.xml)不要修改 src目录下
xml中配置(xml中url报错需要设置 & 和?改为&和?&)
- 提供获取连接的四个基本信息
- 进行数据库连接池管理的基本信息
- `<?xml version="1.0" encoding="UTF-8"?>
<property name="driverClass">com.mysql.cj.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localHost:3306/test?
&useSSL=false&serverTimezone=UTC
&rewriteBatchedStatements=true</property>
<property name="user">root</property>
<property name="password">root</property>
<!-- 当数据库连接池中连接数不够时 C3P0一次性向服务器申请的连接数 -->
<property name="acquireIncrement">50</property>
<!-- 初始化的连接数 -->
<property name="initialPoolSize">10</property>
<!-- c3p0数据库连接池维护最小的连接数 -->
<property name="minPoolSize">10</property>
<!-- c3p0数据库连接池维护最多的连接数 -->
<property name="maxPoolSize">100</property>
<!-- C3P0连接池最多维护的Statement的个数 -->
<property name="maxStatements">50</property>
<!-- 每个连接中最多可以使用的Statement的个数 -->
<property name="maxStatementsPerConnection">2</property>
`
调用配置文件
public static void main(String[] args) throws SQLException {
ComboPooledDataSource cpds = new ComboPooledDataSource("intergalactoApp");
Connection conn = cpds.getConnection();
System.out.println(conn);
这里的"intergalactoApp"是配置文件取的名字
封装一下 注意 看清楚public static ComboPooledDataSource cpds = new ComboPooledDataSource(“intergalactoApp”);这一行在方法外面因为数据库连接池提供一个即可
public class C3P0Utils {
//使用C3P0数据库连接池技术
public static ComboPooledDataSource cpds = new ComboPooledDataSource("intergalactoApp");
public static Connection getConnection1() throws SQLException{
Connection conn = cpds.getConnection();
return conn;
}
DBCP连接池
导两个jar包
dbcp连接池常用基本配置属性
1.initialSize :连接池启动时创建的初始化连接数量(默认值为0)
2.maxActive :连接池中可同时连接的最大的连接数(默认值为8,调整为20,高峰单机器在20并发左右,自己根据应用场景定)
3.maxIdle:连接池中最大的空闲的连接数,超过的空闲连接将被释放,如果设置为负数表示不限制(默认为8个,maxIdle不能设置太小,因为假如在高负载的情况下,连接的打开时间比关闭的时间快,会引起连接池中idle的个数 上升超过maxIdle,而造成频繁的连接销毁和创建,类似于jvm参数中的Xmx设置)
4.minIdle:连接池中最小的空闲的连接数,低于这个数量会被创建新的连接(默认为0,调整为5,该参数越接近maxIdle,性能越好,因为连接的创建和销毁,都是需要消耗资源的;但是不能太大,因为在机器很空闲的时候,也会创建低于minidle个数的连接,类似于jvm参数中的Xmn设置)
5.maxWait :最大等待时间,当没有可用连接时,连接池等待连接释放的最大时间,超过该时间限制会抛出异常,如果设置-1表示无限等待(默认为无限,调整为60000ms,避免因线程池不够用,而导致请求被无限制挂起)
6.poolPreparedStatements:开启池的prepared(默认是false,未调整,经过测试,开启后的性能没有关闭的好。)
7.maxOpenPreparedStatements:开启池的prepared 后的同时最大连接数(默认无限制,同上,未配置)
8.minEvictableIdleTimeMillis :连接池中连接,在时间段内一直空闲, 被逐出连接池的时间
9.removeAbandonedTimeout :超过时间限制,回收没有用(废弃)的连接(默认为 300秒,调整为180)
10.removeAbandoned :超过removeAbandonedTimeout时间后,是否进 行没用连接(废弃)的回收(默认为
方式一(不推荐)
public static void main(String[] args) throws SQLException {
//创建了DBCP的数据库连接池
BasicDataSource source =new BasicDataSource();
source.setDriverClassName("com.mysql.cj.jdbc.Driver");
source.setUrl("jdbc:mysql://localhost:3306/test?useSSL=false"
+ "&serverTimezone=UTC&rewriteBatchedStatements=true");
source.setUsername("root");
source.setPassword("root");
//还可以设置其他关于数据库管理的操作
source.setInitialSize(10);
source.setMaxActive(10);
//......
Connection conn = source.getConnection();
//设置基本信息
System.out.println(conn);
}
方式二 使用配置文件
还可以加其他操作DBCP的属性
public static void main(String[] args) throws Exception {
Properties pros = new Properties();
FileInputStream is =new FileInputStream(new File("src/dbcp.properties"));
pros.load(is);
DataSource source = BasicDataSourceFactory.createDataSource(pros);
Connection conn = source.getConnection();
System.out.println(conn);
}
封装
需要使用 静态代码块 随着类的加载而加载 只调用一次
public class DBCPUtils {
private static DataSource source;
static{
try {
Properties pros = new Properties();
FileInputStream is =new FileInputStream(new File("src/dbcp.properties"));
pros.load(is);
source= BasicDataSourceFactory.createDataSource(pros);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static Connection getConnection2() throws Exception{
Connection conn = source.getConnection();
System.out.println(conn);
return conn;
}
}
Durid(德鲁伊)
1导驱动
常用的配置参数
参数 说明
url 连接字符串
username 用户名
password 密码
driverClassName 驱动类名,会自动根据URL识别,这一项可以不配置
initialSize 初始连接数
maxActive 最大连接数
maxWait 最长等待时间
package 数据库连接池;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.sql.Connection;
import java.util.Properties;
import javax.sql.DataSource;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
public class DruidTest {
public static void main(String[] args) throws Exception {
Properties pros =new Properties();
FileInputStream f = new FileInputStream(new File("src/druid.properties"));
pros.load(f);
DataSource source = DruidDataSourceFactory.createDataSource(pros);
Connection conn = source.getConnection();
System.out.println(conn);
}
}
封装
package 数据库连接池;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
import javax.sql.DataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
public class DruidUtils {
private static DataSource source1;
static{
try {
Properties pros =new Properties();
FileInputStream f = new FileInputStream(new File("src/druid.properties"));
pros.load(f);
source1 = DruidDataSourceFactory.createDataSource(pros);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static Connection getConnection3() throws SQLException
{
Connection conn = source1.getConnection();
return null;
}
}