插入大量数据时,建议使用批处理来做
statement.addBatch();//添加批处理,先将数据缓存起来
statement.executeBatch();//执行批处理
statement.clearBatch();//清空缓存
调用存储过程 {call [(,, ...)]}
Connection conn = JDBCUtils.getConnection();
String sql="{call testPro(?,?)}"; //输入或输出参数用?占位
CallableStatement prepareCall = conn.prepareCall(sql);
//给输入参数设置值
prepareCall.setInt(1,7369);
//如果有输出参数我们需要注册输出参数
prepareCall.registerOutParameter(2, Types.INTEGER);
boolean b = prepareCall.execute();
//获取输出结果
int r = prepareCall.getInt(2);
System.out.println(r);
//释放资源
JDBCUtils.close(conn,prepareCall);
调用自定义函数 {?=call[( < arg1 >,, ...)]}
String sql="{?=call testFun(?)}";
CallableStatement callableStatement = conn.prepareCall(sql);
//设置输入参数
callableStatement.setInt(2,7902);
//注册返回值
callableStatement.registerOutParameter(1, Types.INTEGER);
callableStatement.execute();
//获取返回的结果
int r = callableStatement.getInt(1);
System.out.println("结果是:"+r);
//释放资源
JDBCUtils.close(conn,callableStatement);
要获取自增长键的值 需要在获取操作对象时声明一个参数 Statement.RETURN_GENERATED_KEYS
PreparedStatement preparedStatement = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
2.当数据插入成功后,就可以取出这个自增长键的值
//获取自增长键的结果集
ResultSet generatedKeys = preparedStatement.getGeneratedKeys();
while (generatedKeys.next()){
keyValue = generatedKeys.getInt(1);
}
3.你在其他表中就可以使用这个自增长键的值
基本概念:
事务使指一组最小逻辑操作单元,里面有多个操作组成。组成事务的每一部分必须要同时提交成功,如果有一个操作失败,整个操作就回滚。
事务ACID特性
原子性(Atomicity)
原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
一致性(Consistency)
事务必须使数据库从一个一致性状态变换到另外一个一致性状态。
隔离性(Isolation)
事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。
持久性(Durability)
持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响
默认情况下,Connection 对象处于自动提交模式下,
这意味着它在执行每个语句后都会自动提交更改。
如果禁用了自动提交模式,那么要提交更改就必须显式调用 commit 方法;否则无法保存数据库更改。
//void setAutoCommit ( boolean autoCommit)
throws SQLException将此连接的自动提交模式设置为给定状态。如果连接处于自动提交模式下,则它的所有 SQL 语句将被执行并作为单个事务提交。否则,它的 SQL 语句将聚集到事务中,
//直到调用 commit 方法或 rollback 方法为止。默认情况下,新连接处于自动提交模式。
`案例:扣钱,加钱 转账案例
将扣钱,加钱放入同一个事务中,转账过程中碰到异常时,事务回滚,返回原先值;
回滚事务:rollback();
手动提交事务:commit();
取消自动提交事务:SetAutoCommit(false);
代码实现:
public class pra {
public static void main(String[] args) {
Connection con=null;
PreparedStatement statement1 =null;
PreparedStatement statement2 =null;
try {
Class.forName("com.mysql.jdbc.Driver");
con=DriverManager.getConnection("jdbc:mysql://localhost:3306/lala","root","123456");
con.setAutoCommit(false);//取消自动提交事务
String sql1="update bank set money=money-10000 where name='zhangsan' ";
statement1 = con.prepareStatement(sql1);
statement1.executeUpdate();
System.out.println(1/0);
String sql2="update bank set money=money+10000 where name='lisi' ";
statement2 = con.prepareStatement(sql2);
statement2.executeUpdate();
} catch (Exception e) {
try {
con.rollback(); //设置事务回滚,遇到异常时,事务回滚,数据库中的数据不变
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
} finally {
try {
con.commit();//手动提交事务
con.close();
statement1.close();
statement2.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
设置回滚点:setSavepoint();
回滚到指定回滚点:rollback(回滚点变量名);
代码实现:
public class pra2 {
public static void main(String[] args) {
Connection con=null;
PreparedStatement statement=null;
PreparedStatement statement1=null;
PreparedStatement statement2=null;
PreparedStatement statement3=null;
Savepoint savepoint=null;
try {
Class.forName("com.mysql.jdbc.Driver");
con = DriverManager.getConnection("jdbc:mysql:///lala", "root", "123456");
con.setAutoCommit(false);
String sql1="update bank set money=money-5000 where name='zhangsan'";
statement = con.prepareStatement(sql1);
statement.executeUpdate();
String sql2="update bank set money=money+5000 where name='lisi'";
statement1 = con.prepareStatement(sql2);
statement1.executeUpdate();
savepoint= con.setSavepoint();
System.out.println(1/0);
String sql3="update bank set money=money-5000 where name='zhangsan'";
statement2 = con.prepareStatement(sql3);
statement2.executeUpdate();
String sql4="update bank set money=money+5000 where name='lisi'";
statement3 = con.prepareStatement(sql4);
statement3.executeUpdate();
} catch (Exception e) {
try {
con.rollback(savepoint);
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
} finally {
try {
con.commit();
statement.close();
statement1.close();
statement2.close();
statement3.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
事务总结:
事务的特性:★★★
ACID
原子性:事务里面的操作单元不可切割,要么全部成功,要么全部失败
一致性:事务执行前后,业务状态和其他业务状态保持一致.
隔离性:一个事务执行的时候最好不要受到其他事务的影响
持久性:一旦事务提交或者回滚.这个状态都要持久化到数据库中
不考虑隔离性会出现的读问题★★
脏读:在一个事务中读取到另一个事务没有提交的数据
不可重复读:在一个事务中,两次查询的结果不一致(针对的update操作) 不可重复读,是指在数据库访问中,一个事务范围内两个相同的查询却返回了不同数据。
虚读(幻读):在一个事务中,两次查询的结果不一致(针对的insert操作) 无法演示出来,MySQL已经默认避免了
MySQL 有四种隔离级别
通过设置数据库的隔离级别来避免上面的问题(理解)
read uncommitted 读未提交 上面的三个问题都会出现
read committed 读已提交 可以避免脏读的发生 Oracle 默认级别
repeatable read 可重复读 可以避免脏读和不可重复读的发生 MySQL 默认级别
serializable 串行化 可以避免所有的问题
了解
演示脏读的发生:
将数据库的隔离级别设置成 读未提交
set session transaction isolation level read uncommitted;
查看数据库的隔离级别
select @@tx_isolation;
演示:
打开两个窗口进行演示:给两个窗口设置好同的隔离级别
开启事务 start transaction;
修改数据:update bank set money=1500 where username='lisi';
让另一个窗口开启事务 查询数据 他查到了 就是脏读
我这边窗口 一回滚(rollback),钱又没过去
避免脏读的发生,将隔离级别设置成 读已提交
set session transaction isolation level read committed;
不可避免不可重复读的发生.
避免不可重复读的发生 经隔离级别设置成 可重复读
set session transaction isolation level repeatable read;
演示串行化 可以避免所有的问题
set session transaction isolation level serializable; 我这边的事务不提交,那边的事务无法执行
锁表的操作.
四种隔离级别的效率
read uncommitted>read committed>repeatable read>serializable
四种隔离级别的安全性
read uncommitted
为什么要有连接池?
由于建立数据库连接是一种非常耗时、耗资源的行为,
所以通过连接池预先同数据库建立一些连接,放在内存中,应用程序需要建立数据库连接时直接到连接池中申请一个就行,使用完毕后再归还到连接池中。
1.DBCP连接池
DBCP(DataBase Connection Pool)数据库连接池,
是Java数据库连接池的一种,由Apache开发,通过数据库连接池,
可以让程序自动管理数据库连接的释放和断开
使用步骤:
1.导入jar包(commons-dbcp-1.4.jar和commons-pool-1.5.6.jar)
2.使用api
a.硬编码
a.硬编码(不推荐)
//创建连接池
BasicDataSource ds = new BasicDataSource();
//配置信息
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql:///day07");
ds.setUsername("root");
ds.setPassword("1234");
Connection conn=ds.getConnection();
String sql="insert into category values(?,?);";
PreparedStatement st=conn.prepareStatement(sql);
//设置参数
st.setString(1, "c011");
st.setString(2, "饮料");
int i = st.executeUpdate();
System.out.println(i);
*** 采用配置文件的方式
//存放配置文件
Properties prop = new Properties();
prop.load(new FileInputStream("src/dbcp.properties"));
//设置
//prop.setProperty("driverClassName", "com.mysql.jdbc.Driver");
//创建连接池
DataSource ds = new BasicDataSourceFactory().createDataSource(prop);
Connection conn=ds.getConnection();
String sql="insert into category values(?,?);";
PreparedStatement st=conn.prepareStatement(sql);
//设置参数
st.setString(1, "c012");
st.setString(2, "饮料1");
int i = st.executeUpdate();
System.out.println(i);
2.C3P0
C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。目前使用它的开源项目有Hibernate、Spring等。
使用步骤:
1.导入jar包(c3p0-0.9.1.2.jar)
2.使用api
方式1:硬编码
ComboPooledDataSource ds = new ComboPooledDataSource();
//设置基本参数
ds.setDriverClass("com.mysql.jdbc.Driver");
ds.setJdbcUrl("jdbc:mysql:///day07");
ds.setUser("root");
ds.setPassword("1234");
Connection conn=ds.getConnection();
String sql="insert into category values(?,?);";
PreparedStatement st=conn.prepareStatement(sql);
//设置参数
st.setString(1, "c013");
st.setString(2, "毒药");
int i = st.executeUpdate();
System.out.println(i);
*** 采用配置文件的方式
b.配置文件
要求1:配置文件的名称:c3p0.properties 或者 c3p0-config.xml
要求2:配置文件的路径:src下
编码只需要一句话
new ComboPooledDataSource()//使用默认的配置
//可以切换xml配置文件中的标签 来加载不同的配置比如更换Orecal的数据库的配置
new ComboPooledDataSource(String configName)//使用命名的配置 若配置的名字找不到,使用默认的配置
3.Druid 阿里德鲁伊连接池
DRUID是阿里巴巴开源平台上一个数据库连接池实现,它结合了C3P0、DBCP、PROXOOL等DB池的优点,同时加入了日志监控,可以很好的监控DB池连接和SQL的执行情况,可以说是针对监控而生的DB连接池(据说是目前最好的连接池,不知道速度有没有BoneCP快)。
使用步骤,导入德鲁伊的jar包
1.采用硬编码的方式
//采用硬编码的方式来创建连接池
DruidDataSource dataSource = new DruidDataSource();
//设置参数
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUsername("root");
dataSource.setPassword("123456");
dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/crm");
dataSource.setInitialSize(5);
dataSource.setMinIdle(1);
dataSource.setMaxActive(10);
//获取连接对象
DruidPooledConnection connection = dataSource.getConnection();
System.out.println(connection);
2.采用配置文件的方式,
a.引入配置文件
b.加载配置文件
//采用加载配置文件的方式
Properties properties = new Properties();
properties.load(new FileInputStream("src/db_server.properties"));
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
Connection conn = dataSource.getConnection();
System.out.println(conn);
德鲁伊连接池代码实现:
硬编码代码实现:
public class test {
public static void main(String[] args) throws SQLException {
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql:///lala");
ds.setUsername("root");
ds.setPassword("123456");
DruidPooledConnection connection = ds.getConnection();
PreparedStatement statement = connection.prepareStatement("select * from bank ;");
ResultSet resultSet = statement.executeQuery();
while(resultSet.next()){
String name = resultSet.getString(1);
int money = resultSet.getInt(2);
System.out.println("姓名:"+name+"存款:"+money);
}
}
}
//配置文件代码实现
//通过一个工厂类,创建一个数据源
//DruidDataSourceFactory().createDataSource(参数)
//然后获取连接对象
Properties properties = new Properties();
properties.load(new FileReader("pro,pro"));
//建立工厂,生成数据源
DataSource ds = new DruidDataSourceFactory().createDataSource(properties);
//获取连接
Connection connection = ds.getConnection();
//获取预编译对象
PreparedStatement statement = connection.prepareStatement("select * from bank ;");
ResultSet resultSet = statement.executeQuery();
while(resultSet.next()){
String name = resultSet.getString(1);
int money = resultSet.getInt(2);
System.out.println("姓名:"+name+"存款:"+money);
}
注意:Druid线程池,使用配置文件方法连接时,参数名固定为:driverClassName,url,username,password
Commons DbUtils是Apache组织提供的一个对JDBC进行简单封装的开源工具类库,
使用它能够简化JDBC应用程序的开发,同时也不会影响程序的性能。
使用步骤:
1.导入jar包(commons-dbutils-1.4.jar)
2.创建一个queryrunner类
queryrunner作用:操作sql语句
构造方法:
new QueryRunner(Datasource ds);
3.编写sql
4.执行sql
query(..):执行查询操作
update(...):执行增,删,改操作
使用步骤
// 创建对象
QueryRunner runner = new QueryRunner(new ComboPooledDataSource());
// 定义sql
String sql = "insert into bank(username,money) values(?,?)";
// 执行sql
runner.update(sql,"赵六",500);
....
执行查询后返回的结果集
ResultSetHandler(存储对象的字节码对象):封装结果集 接口,每次封装一条记录
BeanListHandler(存储对象的字节码对象), 将查询结果的每一条记录封装成指定的bean对象,将每一个bean对象放入list中 返回.
//update方法代码实现
Properties properties = new Properties();
properties.load(new FileReader("pro,pro"));
//建立工厂,生成数据源
DataSource ds = new DruidDataSourceFactory().createDataSource(properties);
//rr(ds);
QueryRunner queryRunner = new QueryRunner(ds);
int i = queryRunner.update("update bank set money=0 where name=?", "lisi");
if(i>0){
System.out.println("更新成功!");
}else{
System.out.println("饿了");
}
//查询表中所有数据代码实现
Properties properties = new Properties();
properties.load(new FileReader("pro,pro"));
//建立工厂,生成数据源
DataSource ds = new DruidDataSourceFactory().createDataSource(properties);
//rr(ds);
QueryRunner queryRunner = new QueryRunner(ds);
List<bank> query = queryRunner.query("select * from bank;", new BeanListHandler<bank>(bank.class));
for (bank bank : query) {
System.out.println(bank.getName()+"==="+bank.getMoney());
}
//查询表中单条数据代码实现
Properties properties = new Properties();
properties.load(new FileReader("pro,pro"));
//建立工厂,生成数据源
DataSource ds = new DruidDataSourceFactory().createDataSource(properties);
//rr(ds);
QueryRunner queryRunner = new QueryRunner(ds);
bank bank = queryRunner.query("select * from bank where name=?;", new BeanHandler<bank>(bank.class), "lisi");
System.out.println(bank.getName()+"==="+bank.getMoney());