JDBC 全称为:Java DataBase Connectivity,它是可以执⾏SQL 语句的Java API。由Sun公司提供接口,数据库厂商负责实现,在使用不同数据库时,只要用数据库厂商提供的数据库驱动程序即可,大大简化学习成本。
//简单示例
Class.forName(come.mysql.jdbc.Driver);
String username="root";
String password="123456";
String connectUrl="jdbc:mysql://127.0.0.1:3306/db_test?useUnicode=true&characterEncodeing=UTF-8";
Connection conn=DriverManager.getConnection(connectionUrl,username,password);
System.out.println("连接成功");
conn.close();
system.out.println("关闭连接");
客户端与数据库所有的交互都是通过Connection 来完成的。常用方法如下:
//创建向数据库发送sql 的statement对象。
createcreateStatement ()
//创建向数据库发送预编译sql 的PrepareSatement对象。
prepareStatement (sql )
//创建执⾏存储过程的callableStatement对象
prepareCall (sql )
//设置事务⾃动提交
setAutoCommit (boolean autoCommit)
//提交事务
commit ()
//回滚事务
rollback (
Statement对象⽤于向数据库发送Sql语句,对数据库的增删改查都可以通过此对象发送sql语句完成,方法如下:
//查询
executeQuery(String sql )
//增删改
executeUpdate (String sql )
//任意sql 语句都可以,但是⽬标不明确,很少⽤
execute(String sql )
//把多条的sql 语句放进同⼀个批处理中
addBatch (String sql )
//向数据库发送⼀批sql 语句执⾏
executeBatch()
PreparedStatement 对象继承Statement对象,它⽐Statement对象更强⼤,使⽤起来更简单
//模拟查询id为2的信息
String id = "2" ;
Connection connection = UtilsDemo.getConnection ();
String sql = "SELECT * FROM users WHERE id = ?";
PreparedStatement preparedStatement =
connection.preparedStatement(sql );
//第⼀个参数表示第⼏个占位符【也就是?号】,第⼆个参数表示值是多少
preparedStatement.setString(1,id);
ResultSet resultSet = preparedStatement.executeQuery()
//模拟查询id为2的信息
String id = "2" ;
Connection connection = UtilsDemo.getConnection ();
String sql = "SELECT * FROM users WHERE id = ?";
PreparedStatement preparedStatement =
connection.preparedStatement(sql );
//第⼀个参数表示第⼏个占位符【也就是?号】,第⼆个参数表示值是多少
preparedStatement.setString(1,id);
ResultSet resultSet = preparedStatement.executeQuery()
ResultSet 对象代表Sql语句的执⾏结果,当Statement 对象执⾏executeQuery()
时,会返回⼀个ResultSet对象
ResultSet对象维护了⼀个数据⾏的游标【简单理解成指针】,调用ResultSet.next()
⽅法,可以让游标指向具体的数据⾏,进⾏获取该⾏的数据。
对列字段的描述信息,在结果集中,不但由每一行的数据,还有元数据信息。包括列名、列标题、列类型等。对于增删改⽽⾔,只有SQL 和参数是不同的,我们为何不把这些相同的代码抽取成⼀个⽅法?对于查询⽽⾔,不同的实体查询出来的结果集是不⼀样的。我们要使⽤元数据获取结果集的信息,才能对结果集进⾏操作。
数据库的连接的建⽴和关闭是⾮常消耗资源的、频繁地打开、关闭连接造成系统性能低下。
每一个JDBC连接,内部实现上是创建了一个TCP/Socket连接。在MySQL中可以查看连接的情况。一个连接上可以执行多次SQL交互,连接要及时关闭,否则会导致连接量过大,到达上限后便不会接收新的连接。
SHOW STATUS LIKE 'Threads%' /*连接数量*/
SHOW processlist /*连接详情*/
SHOW full processlist
SHOW VARIABLES LIKE 'max_connections' /*查看最大连接数*/
连接超时:当一个连接到MySQL之后,如果长时间未作任何SQL交互,会被认定超时而断开。其中,wait_timeout即为超时时间的设定
SHOW GLOBAL VARIABLES LIKES '%timeout%'
JDBC两种连接方式:
//一个DataSource指代一个数据源,内部有一个连接池,自动读取c3p0-config.xml的配置
ComboPooledDataSource pool=new ComboPooledDataSource();
//一个connection代表一次访问
Connection conn=pool.getConnection();
//下面建立statement对象,通过查询生成ResultSet
//用完之后
conn.close();//连接放回池子
pool.close();
一个业务单元:需要一次性地执行多个SQL操作,修改多个有关系的表。
事务的一般使用模式:
beginTrasaction();//开始事务
try{
sql1;
sql2;
sql3;
commit()//提交,确认所有修改
}catch(e){
rollback() //回滚,撤销所有修改
}
举例:A向B转账,转账这个流程中如果出现问题,事务可以让数据恢复成⼀样【A账户的钱没变,B账户的钱也没变】。
/*
* 我们来模拟A向B账号转账的场景
* A和B账户都有1000块,现在我让A账户向B账号转500 块钱
*
* */
//JDBC默认的情况下是关闭事务的,下⾯我们看看关闭事务去操作转账操作有什么问题
//A 账户减去500 块
String sql = "UPDATE a SET money=money-500 " ;
preparedStatement = connection.prepareStatement (sql );
preparedStatement.executeUpdate ();
//B 账户多了500 块
String sql2 = "UPDATE b SET money=money+500";
preparedStatement = connection.prepareStatement (sql2);
preparedStatement.executeUpdate ();
模拟出现问题的情况,显然,上⾯代码是会抛出异常的,我们再来查询⼀下数据。A账户少了500 块钱,B账户的钱没有增加。这明显是不合理的。
//A 账户减去500 块
String sql = "UPDATE a SET money=money-500 " ;
preparedStatement = connection.prepareStatement (sql );
preparedStatement.executeUpdate ();
//这⾥模拟出现问题
int a = 3 / 0;
String sql2 = "UPDATE b SET money=money+500";
preparedStatement = connection.prepareStatement (sql2);
preparedStatement.executeUpdate ();
通过事务解决上面的问题
//开启事务,对数据的操作就不会⽴即⽣效。
connection.setAutoCommit (false);
//A 账户减去500 块
String sql = "UPDATE a SET money=money-500 " ;
preparedStatement = connection.prepareStatement (sql );
preparedStatement.executeUpdate ();
//在转账过程中出现问题
int a = 3 / 0;
//B 账户多500 块
String sql2 = "UPDATE b SET money=money+500";
preparedStatement = connection.prepareStatement (sql2);
preparedStatement.executeUpdate ();
//如果程序能执⾏到这⾥,没有抛出异常,我们就提交数据
connection.commit ();
//关闭事务【⾃动提交】
connection.setAutoCommit (true);
} catch (SQLException e) {
try {
//如果出现了异常,就会进到这⾥来,我们就把事务回滚【将数据变成原来那样】
connection.rollback ();
//关闭事务【⾃动提交】
connection.setAutoCommit (true);
} catch (SQLException e1) {
e1.printStackTrace();
}
上⾯的程序也⼀样抛出了异常,A账户钱没有减少,B账户的钱也没有增加。
注意:当Connection遇到⼀个未处理的SQLException时,系统会⾮正常退出,事务也会⾃动回滚,但如果程序捕获到了异常,是需要在catch 中显式回滚事务的.
数据库定义了4个隔离级别:
分别对应Connection类中的4个常量
脏读:一个事务读取到另外一个事务未提交的数据
不可重复读:⼀个事务读取到另外⼀个事务已经提交的数据,也就是说⼀个事务可以看到其他事务所做的修改
虚读(幻读):是指在⼀个事务内读取到了别的事务插⼊的数据,导致前后读取不⼀致。注:和不可重复读类似,但虚读(幻读)会读到其他事务的插⼊的数据,导致前后读取不⼀致
MySQL数据库从入门到实战应用
注:关于事务更多问题在下一篇数据库相关文章中进行总结!