一、JDBC简介
1、JDBC是SUN公司为了简化操作数据推出一套规范。数据库厂商的驱动就是对JDBC的实现。
2、Java Data Base Connectivity(java数据库连接),它主要由接口组成。
java.sql.* javax.sql.* JDK中数据库的驱动jar包
二、JDBC的编码步骤
前提:拷贝数据库的驱动到构建路径中(classpath)
1、注册驱动
2、获取与数据库的链接
3、创建代表SQL语句的对象
4、执行SQL语句
5、如果是查询语句,需要遍历结果集
6、释放占用的资源
三、JDBC中常用的接口详解
1、DriverManager作用
⑴注册驱动:
方式一(不建议使用):DriverManager.registerDriver(new com.mysql.jdbc.Driver());
原因:①依赖具体驱动。②导致驱动注册2遍,从源码里看(如下图)。
方式二(建议):Class.forName("com.mysql.jdbc.Driver");
只依赖字符串,可以实现运行时依赖,不用编码依赖(如下图)。
⑵获取与数据库的链接
url:SUN和数据库厂商间的协议。
MySql:jdbc:mysql://localhost:3306/test
Oracle: jdbc:oracle:thin:@localhost:1521/test
public static Connection getConnection(String url,String user,String password) throws SQLException
public static Connection getConnection(String url,Properties info) throws SQLException
public static Connection getConnection(String url) throws SQLException
2、Connection
所有的数据库操作都是基于链接之上的。
Statement createStatement():创建向数据库发送sql的statement对象。即Statement stmt = conn.createStatement();//创建向数据库发送sql的statement对象
preparedStatement(sql): 创建向数据库发送预编译sql的Prepare Statement对象。
preparedCall(sql): 创建执行存储过程的Callable Statement对象
setAutoCommit(boolean autoCommit):设置事务是否自动提交
commit():在链接上提交事务
rollback():在此链接上回滚事务
3、Statement
作用:代表SQL语句对象。可以向数据库发送任何的SQL语句
ResultSet executeQuery(String sql):执行sql查询,sql一般都是查询语句,返回ResultSet
int executeUpdate(String sql):sql一般是DML(insert update delete)语句。返回int值,操作几条记录。
boolean execute(String sql):sql可以是任意的语句。返回值不是代表成功与否。如果是查询语句,就有结果集,返回true。没有返回结果集的,返回false。
addBatch(String sql):把多条sql语句放到一个批处理中。
executeBatch():向数据库发送一批sql语句执行。
4、ResultSet
作用:封装了查询的结果集。封装执行结果时,采用类似表格的方式。ResultSet对象维护了一个指向数据行的游标,默认指向表格第一行数据内容的前面。
方法:X getX(String columnName或 int columnIndex) 其中X为String,int,long等类型
补充,游标的操作:
boolean next():游标下移。返回值是有无记录
boolean previous():游标上移。
boolean absolute(int count):定位到指定的行。第一行是1。
void beforeFirst():移动游标到第一行的前面。
void afterLast():移动游标到最后一行的后面。
四、释放占用的资源
一定要释放资源,所以写到finally里。
五、SQL注入
SQL注入攻击指的是黑客通过构建特殊的命令作为参数传入Web程序,而这些命令大都是SQL语法里的一些基本组合。当条件为' or 1=1 and id ='可以当条件查询。
如:select * from user where name='' or 'a'='a';
select * from user where name='' or 1=1
等价于 select * from user
SQL注入分为平台注入和代码层注入。前者是由不安全的数据库配置或数据库平台的漏洞导致;后者主要是由于程序员对输入未进行细致的过滤,从而执行了非法执行。
产生原因:
①不当的类型处理
②不安全的数据库配置
③不合理的查询集处理
④不当的错误处理
⑤转义字符处理不合适
⑥多个提交处理不当
SQL注入的预防:
①用户的表单输入域:防止一些特殊字符。
②对密码加密:MD5加密
③PreparedStatement预编译(得到该对象时,就必须给他SQL语句)
支持参数占位符: ? 一个问号代表着一个参数。
注:能用PreparedStatement就不要用Statement。
六、如何调用存储过程
Mysql:
inputParam为输入参数,inOutParam既可以作为输出参数,又可以作为输入参数。
Oracle:
七、事务
1、事务指逻辑上的一组操作,组成这组操作的各个单元,要么全部成功,要么全部不成功。
MySQL:每一条语句都属于独立事务,默认自动管理的。
2、相关操作内容:
开启事务:start transaction; //日后的语句都会处于同一个事务之中。
提交事务:commit;
回滚事务:rollback;
3、事务的特性(数据安全):
原子性:处于事务中的多条语句是不可分割的。(当做一个整体对待,操作事务要么都发生,要么都不发生)
一致性:事务必须使数据库从一个一致性状态变换到另外一个一致性状态。比如:转账,转账前A+B=2000,转账后A+B=2000
隔离性:多线程并发。一个事务不能被其他线程中的事务所打扰。
持久性:事务一旦提交,永久保存起来。(保存到硬盘,而不是内存)
4、事务的隔离级别:属于事务的。都已开启了事务为前提。
不考虑事务的隔离级别,会出现以下情况(是错的)
- 脏读:一个线程中的事务读到了另外一个线程中未提交的数据。
- 不可重复读:一个线程中的事务读到了另外一个线程中已经提交的update的数据。
- 虚读:一个线程中的事务读到了另外一个线程中已经提交的insert的数据。
要想避免以上现象,通过更改事务的隔离级别来避免:()
- READ UNCOMMITTED “脏读、不可重复读、虚读”都有可能发生。
- READ COMMITTED 避免脏读的发生,“不可重复读、虚读”有可能发生。
- REPEATABLE READ 避免“脏读、不可重复读”的发生,虚读有可能发生。
- SERIALIZABLE 避免“脏读、不可重复读、虚读”的发生。
级别依次升高,效率依次降低,数据越安全。
MySQL:默认REPEATABLE READ
ORACLE:默认READ COMMITTED
⑴MySQL:
select @@tx_isolation;//查看当前会话的隔离级别
set transaction isolation level 级别;// 设置当前的事务隔离级别
⑵Oracle:
1.查看系统默认事务隔离级别,也是当前会话隔离级别
--首先创建一个事务
declare
trans_id Varchar2(100);
begin
trans_id := dbms_transaction.local_transaction_id( TRUE );
end;
--查看事务隔离级别
SELECT s.sid, s.serial#,
CASE BITAND(t.flag, POWER(2, 28))
WHEN 0 THEN 'READ COMMITTED'
ELSE 'SERIALIZABLE'
END AS isolation_level
FROM v$transaction t
JOIN v$session s ON t.addr = s.taddr AND s.sid = sys_context('USERENV', 'SID');
八、连接池
- 连接池:是创建和管理一个连接的缓冲池的技术。
- 编写连接池:
用一个实现了javax.sql.DataSource类的实例时,用户如果调用Connection.close()方法,会把链接关闭,失去了连接池的意义,而我们需要实现完实例后,放回连接池里,方便下一次的使用,而不是立马关闭。所以使用包装设计模式,而包装设计模式的核心是保持被包装对象的原有信息,又可以对某个或某些方法进行改写。
⑴流程如下,即用包装(装饰)设计模式:
①编写一个类,实现与被包装类(即数据库驱动对Connection的实现)相同的接口。(使这个类和数据库的驱动实现有着相同的行为)
②定义一个变量,引用被包装类的实例。
③定义构造方法,传入被包装类的实例。
④对于要改写的方法,编写自己的代码即可。
⑤对于不需要改写的方法,调用原有对象的对应方法。
⑵为何不用继承的方法来改写close()方法:
①因为数据库的种类比较多,要写继承的话会写死,因为他是继承相对应数据库的架包里的方法。
②数据库驱动对Connection接口的实现类,不允许被继承
③假设能继承成功了,丢失了原有对象的信息。
3.连接池的动态代理(Aspect-Oriented Programming,简称AOP,即默认适配器设计模式)
3.1动态代理技术介绍
3.1.1代理模式:
就是为其他对象提供一种代理以控制对这个对象的访问。即明星与经纪人关系,经纪人即代理,联系代理,由代理转交给明星。
3.1.2基于接口的动态代理:Proxy;如果一个类没实现任何的接口,此种代理就不能使用了。
说明:Proxy创建动态代理类的实例提供了静态方法,也是所有动态代理类的父类的方法创建。是JDK自带的。
InvocationHandler handler = new MyInvocationHandler(...);
Class proxyClass = Proxy.getProxyClass(Foo.class.getClassLoader(), new Class[] { Foo.class });
Foo f = (Foo) proxyClass.getConstructor(new Class[] { InvocationHandler.class }).
newInstance(new Object[] { handler });
或者更简单的:
Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(), new Class[] { Foo.class },handler);
3.2基于接口的连接池动态代理:Proxy
3.3基于子类的动态代理:CGLIB;子类可以扩展父类的功能
前提:被代理类的要求
a、不能是final的
b、必须是public的
比如普通的JavaBean就可能没有实现任何的接口。
代理类是被代理类的子类。
4.开源第三方数据源使用(DBCP、C3P0和JDNI)
4.1 JDNI(Java Naming and DIrectory Interface)
⑴拷贝数据库的驱动到Tomcat\lib目录下
⑵在web应用的META-INF目录下建立一个名称为context.xml的配置文件
⑶获取JNDI容器中的资源
Lookup里的地址后面需要跟第二步里面起的一致。
补充:JDNI的包放在javax.naming.*里,不要在main方法中获取数据源,获取不到。
4.2 DBCP(APache组织实现的。DBCP:DataBase Connection Pool)
需要架包:commons-dbcp-1.4.jar和commons-pool-1.5.6.jar
4.3 C3P0
需要架包:c3p0-0.9.1.2.jar