JDBC全称为:Java DataBase Connectivity(java数据库连接)。
SUN公司为了简化、统一对数据库的操作,定义了一套Java操作数据库的规范,称之为JDBC。
1: 编写JDBC程序
编程从student表中读取数据,并打印在命令行窗口中。
一、搭建实验环境 :
1、在mysql中创建一个库,并创建student表和插入表的数据。
2、新建一个Java工程,并导入数据驱动。
二、编写程序,在程序中加载数据库驱动 DriverManager. registerDriver(Driver driver)
三、建立连接(Connection) Connection conn = DriverManager.getConnection(url,user,pass);
四、创建用于向数据库发送SQL的Statement对象,并发送sql Statement st = conn.createStatement(); ResultSet rs = st.executeQuery(sql);
五、从代表结果集的ResultSet中取出数据,打印到命令行窗口
六、断开与数据库的连接,并释放相关资源
2:JDBC编程步骤
装载驱动程序
建立连接
操作数据
释放资源
3:JDBC访问数据库的流程
通过DriverManager加载驱动程序driver;
通过DriverManager类获得表示数据库连接的Connection类对象;
通过Connection对象绑定要执行的语句,生成Statement类对象;
执行SQL语句,接收执行结果集ResultSet;
可选的对结果集ResultSet类对象的处理;
必要的关闭ResultSet、Statement和Connection
4:
程序详解—DriverManager
Jdbc程序中的DriverManager用于加载驱动,并创建与数据库的链接,这个API的常用方法: DriverManager.registerDriver(new Driver()) DriverManager.getConnection(url, user, password), 注意:在实际开发中并不推荐采用registerDriver方法注册驱动。
原因有二:
一、查看Driver的源代码可以看到,如果采用此种方式,会导致驱动程序注册两次,也就是在内存中会有两个Driver对象。
二、程序依赖mysql的api,脱离mysql的jar包,程序将无法编译,将来程序切换底层数据库将会非常麻烦。 推荐方式:Class.forName(“com.mysql.jdbc.Driver”);采用此种方式不会导致驱动对象在内存中重复出现,并且采用此种方式,程序仅仅只需要一个字符串,不需要依赖具体的驱动,使程序的灵活性更高。 同样,在开发中也不建议采用具体的驱动类型指向getConnection方法返回的connection对象。
5:数据库URL
URL用于标识数据库的位置,程序员通过URL地址告诉JDBC程序连接哪个数据库,URL的写法为: jdbc:mysql:// localhost:3306/test ?key=value
常用数据库URL地址的写法: Oracle写法: jdbc:oracle:thin:@localhost:1521:sid MySql写法: jdbc:mysql://localhost:3306/sid Mysql的url地址的简写形式: jdbc:mysql:///sid 常用属性:useUnicode=true&characterEncoding=UTF-8
6:Connection
Jdbc程序中的Connection,它用于代表数据库的链接,Collection是数据库编程中最重要的一个对象,客户端与数据库所有交互都是通过connection对象完成的,这个对象的常用方法: createStatement():创建向数据库发送sql的statement对象 prepareStatement(sql) :创建向数据库发送预编译sql的PrepareSatement对象 prepareCall(sql):创建执行存储过程的callableStatement对象。
--- 存储过程 setAutoCommit(boolean autoCommit):设置事务是否自动提交。
commit() :在链接上提交事务。
---与事务相关!! rollback() :在此链接上回滚事务。
7:
Statement -- 一次操作
Jdbc程序中的Statement对象用于向数据库发送SQL语句, Statement对象常用方法: executeQuery(String sql) :用于向数据发送查询语句。 executeUpdate(String sql):用于向数据库发送insert、update或delete语句 execute(String sql):用于向数据库发送任意sql语句
批处理操作
addBatch(String sql) :把多条sql语句放到一个批处理中。
executeBatch():向数据库发送一批sql语句执行。
8:
ResultSet
Jdbc程序中的ResultSet用于代表Sql语句的执行结果。Resultset封装执行结果时,采用的类似于表格的方式。ResultSet 对象维护了一个指向表格数据行的游标cursor,初始的时候,游标在第一行之前,调用ResultSet.next() 方法,可以使游标指向具体的数据行,进而调用方法获取该行的数据。 ResultSet既然用于封装执行结果的,所以该对象提供的大部分方法都是用于获取数据的get方法: 获取任意类型的数据 getObject(int index)
getObject(string columnName) 获取指定类型的数据,例如: getString(int index) getString(String columnName)
9:遍历查询结果
10:
CRUD操作-create
Statement st = conn.createStatement();
String sql = "insert into student(….) values(…..) ";
int num = st.executeUpdate(sql);
if(num>0){
System.out.println("插入成功!!!");
}
11:
CRUD操作-updata
Statement st = conn.createStatement();
String sql = “update student set name=‘’ where name=‘’";
int num = st.executeUpdate(sql);
if(num>0){
System.out.println(“修改成功!!!");
}
12:
CRUD操作-delete
Statement st = conn.createStatement();
String sql = “delete from student where id=1;
int num = st.executeUpdate(sql);
if(num>0){
System.out.println(“删除成功!!!");
}
13:
CRUD操作-read
Statement st = conn.createStatement();
String sql = “select * from student where id=1;
ResultSet rs = st.executeQuery(sql);
while(rs.next()){
//根据获取列的数据类型,分别调用rs的相应方法 //映射到java对象中
}
14:
PreparedStatement
PreparedStatement是Statement的子接口,它的实例对象可以通过调用Connection.preparedStatement(sql)方法获得,相对于Statement对象而言: PreperedStatement:可以避免SQL注入的问题。 Statement会使数据库频繁编译SQL,可能造成数据库缓冲区溢出。PreparedStatement 可对SQL进行预编译,从而提高数据库的执行效率。 并且PreperedStatement对于sql中的参数,允许使用占位符的形式进行替换,简化sql语句的编写。
15:
ResultSet 滚动结果集
ResultSet还提供了对结果集进行滚动和更新的方法 Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);
next():移动到下一行
previous():移动到前一行
absolute(int row):移动到指定行
beforeFirst():移动resultSet的最前面
afterLast() :移动到resultSet的最后面
updateRow() :更新行数据
结果集类型 ResultSet.TYPE_FORWARD_ONLY 只能向前,只能调用next 不能向回滚动 ResultSet.TYPE_SCROLL_INSENSITIVE 支持结果集向回滚动,不能查看修改结果 ResultSet.TYPE_SCROLL_SENSITIVE 支持结果集向回滚动,查看修改结果
结果集并发策略
ResultSet.CONCUR_READ_ONLY 只读
ResultSet.CONCUR_UPDATABLE 支持修改
常见三种组合
ResultSet.TYPE_FORWARD_ONLY 和 ResultSet.CONCUR_READ_ONLY (默认) 只读不支持向回滚动 ResultSet.TYPE_SCROLL_INSENSITIVE 和 ResultSet.CONCUR_READ_ONLY 只读,支持向回滚动 ResultSet.TYPE_SCROLL_SENSITIVE 和 ResultSet.CONCUR_UPDATABLE 支持向回滚动,支持对数据修改
16:
释放资源
Jdbc程序运行完后,切记要释放程序在运行过程中,创建的那些与数据库进行交互的对象,这些对象通常是ResultSet, Statement和Connection对象。
特别是Connection对象,它是非常稀有的资源,用完后必须马上释放,如果Connection不能及时、正确的关闭,极易导致系统宕机。Connection的使用原则是尽量晚创建,尽量早的释放。
为确保资源释放代码能运行,资源释放代码也一定要放在finally语句中。
工具类提取。
17:
事务
事务的概念 事务指逻辑上的一组操作,组成这组操作的各个单元,要不全部成功,要不全部不成功。 例如:A——B转帐,对应于如下两条sql语句
update from account set money=money-100 where name=‘a’;
update from account set money=money+100 where name=‘b’;
数据库默认事务是自动提交的,也就是发一条sql它就执行一条。如果想多条sql放在一个事务中执行,则需要使用如下语句。
数据库开启事务命令 start transaction 开启事务 Rollback 回滚事务 Commit 提交事务
当Jdbc程序向数据库获得一个Connection对象时,默认情况下这个Connection对象会自动向数据库提交在它上面发送的SQL语句。若想关闭这种默认提交方式,让多条SQL在一个事务中执行,可使用下列语句:
JDBC控制事务语句
Connection.setAutoCommit(false); // 相当于start transaction Connection.rollback(); //rollback Connection.commit(); //commit
设置事务回滚点
Savepoint sp = conn.setSavepoint(); Conn.rollback(sp); Conn.commit(); //回滚后必须要提交
事务的特性(ACID)
原子性(Atomicity) 原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。 一致性(Consistency) 事务前后数据的完整性必须保持一致。
隔离性(Isolation) 事务的隔离性是指多个用户并发访问数据库时,一个用户的事务不能被其它用户的事务所干扰,多个并发事务之间数据要相互隔离。
持久性(Durability) 持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响。
事务的隔离级别
多个线程开启各自事务操作数据库中数据时,数据库系统要负责隔离操作,以保证各个线程在获取数据时的准确性。 如果不考虑隔离性,可能会引发如下问题
脏读: 指一个事务读取了另外一个事务未提交的数据。 这是非常危险的,假设A向B转帐100元,对应sql语 句如下所示
1.update account set money=money+100 while name=‘b’;
2.update account set money=money-100 while name=‘a’;
当第1条sql执行完,第2条还没执行(A未提交时),如果此时B查询自己的帐户,就会发现自己多了100元钱。如果A等B走后再回滚,B就会损失100元。
不可重复读: 在一个事务内读取表中的某一行数据,多次读取结果不同。 例如银行想查询A帐户余额,第一次查询A帐户为200元,此时A向帐户存了100元并提交了,银行接着又进行了一次查询,此时A帐户为300元了。银行两次查询不一致,可能就会很困惑,不知道哪次查询是准的。 和脏读的区别是,脏读是读取前一事务未提交的脏数据,不可重复读是重新读取了前一事务已提交的数据。 很多人认为这种情况就对了,无须困惑,当然是后面的为准。我们可以考虑这样一种情况,比如银行程序需要将查询结果分别输出到电脑屏幕和写到文件中,结果在一个事务中针对输出的目的地,进行的两次查询不一致,导致文件和屏幕中的结果不一致,银行工作人员就不知道以哪个为准了。
虚读(幻读):是指在一个事务内读取到了别的事务插入的数据,导致前后读取不一致。 如丙存款100元未提交,这时银行做报表统计account表中所有用户的总额为500元,然后丙提交了,这时银行再统计发现帐户为600元了,造成虚读同样会使银行不知所措,到底以哪个为准。
数据库共定义了四种隔离级别:
Serializable:可避免脏读、不可重复读、虚读情况的发生。
(串行化) Repeatable read:可避免脏读、不可重复读情况的发生。
(可重复读)不可以避免虚读 Read committed:可避免脏读情况发生
(读已提交) Read uncommitted:最低级别,以上情况均无法保证。
(读未提交) set session transaction isolation level
设置事务隔离级别
select @@tx_isolation 查询当前事务隔离级别
18:
数据库连接池编写原理分析
编写连接池需实现javax.sql.DataSource接口。DataSource接口中定义了两个重载的getConnection方法: Connection getConnection() Connection getConnection(String username, String password)
实现DataSource接口,并实现连接池功能的步骤:
在DataSource构造函数中批量创建与数据库的连接,并把创建的连接保存到一个集合对象中 实现getConnection方法,让getConnection方法每次调用时,从集合对象中取一个Connection返回给用户。
当用户使用完Connection,调用Connection.close()方法时,Collection对象应保证将自己返回到连接池的集合对象中,而不要把conn还给数据库。
扩展Connection的close方法 在关闭数据库连接时,将connection存回连接池中,而并非真正的关闭
19:
其他数据库相关
索引,使用索引可快速访问数据库表中的特定信息 为什么用索引? 书本的目录相当于索引,表的数据也需要这样一个目录,这就 是索引。用来加快查询速度。
数据库索引
1.添加PRIMARY KEY(主键索引) mysql>ALTER TABLE `table_name` ADD PRIMARY KEY ( `column` )
2.添加UNIQUE(唯一索引) mysql>ALTER TABLE `table_name` ADD UNIQUE ( `column` )
3.添加INDEX(普通索引) mysql>ALTER TABLE `table_name` ADD INDEX index_name( `column` )
4.添加FULLTEXT(全文索引) mysql>ALTER TABLE `table_name` ADD FULLTEXT ( `column`)
5.添加多列索引 mysql>ALTER TABLE `table_name` ADD INDEX index_name ( `column1`, `column2`, `column3` )