JDBC快速入门二

1. LOB存取

1. 向数据库中插入二进制数据
@Test
public void testInsert() {
	
	Connection conn = null;
	PreparedStatement stmt = null;
	try {
		conn = JDBCUtils.getConnection();
		String sql = "insert into t1 values(?,?)";
		stmt = conn.prepareStatement(sql);
		stmt.setInt(1, 1);
	--------------读取文件中数据-----------------------------	
		InputStream is = new FileInputStream("src/22.jpg");
		stmt.setBinaryStream(2, is,is.available());
	--------------关键两行代码-------------------------------
		stmt.executeUpdate();
		
	} catch(Exception e) {
		throw new RuntimeException(e);
	} finally {
		JDBCUtils.close(null, stmt, conn);
	}
}

2. 向数据库读取二进制文件
conn = JDBCUtils.getConnection();
String sql = "select * from t1 where id=?";
stmt = conn.prepareStatement(sql);
stmt.setInt(1, 1);

rs = stmt.executeQuery();
if (rs.next()) {
------------------------得到查询的iod----------------
	InputStream is = rs.getBinaryStream(2);
	OutputStream os = new FileOutputStream("d:/down.jpg");
------------------------关键两行代码-----------------	
	int len = -1;
	byte buf[] = new byte[1024];
	while((len=is.read(buf)) != -1) {
		os.write(buf,0,len);
		os.flush();
	}
}


3. 关于文本文件读写问题和二进制一样,方法不一样而已
stmt.setCharacterStream(2, reader,(int)file.length());
rs.getCharacterStream(2);

2. Batch批处理

Statement
	1. sql语句固定
	2. 可以一次性批量执行不同的sql语句
	conn = JDBCUtils.getConnection();
	stmt = conn.createStatement();
	String sql = "insert into testbatch value(1,'cgx')";
	String sql2 = "update testbatch set name='aj' where id='1'";
	stmt.addBatch(sql);

	stmt.addBatch(sql2);
	int result[] = stmt.executeBatch();
	for(int i: result) {
		System.out.println(i);
	}

------------------------------------------------
PreparedStatement
	1. sql语句的参数是不确定的
	2. 一次性批量执行相同的sql语句

	conn = JDBCUtils.getConnection();
	stmt = conn.prepareStatement("insert into testbatch values(?,?)");
	
	for(int i=100; i<100; i++ ) {
		stmt.setInt(1,10+i);
		stmt.setString(2,"cgx"+i);
		
		stmt.addBatch();
		if(i%1000==0) {
			//分批执行批处理
			stmt.executeBatch();
			//清空批处理队列
			stmt.clearBatch();
		}
	}
	//执行批处理
	stmt.executeBatch();
	//清空批处理队列
	stmt.clearBatch();

3. 调用存储过程

存储过程
1、存储过程简介(存储过程的创建DBA)
2、运行在服务器上,事先由DBA开发了也编译好了
3、java调用 时,只要知道过程名就可以了
4、提高了安全性

conn = JDBCUtils.getConnection();
stmt = conn.prepareCall("{call demoSp(?,?)}");
stmt.setString(1, "cgx");//输入参数赋值

//对于过程的输出参数要先注册
stmt.registerOutParameter(2, java.sql.Types.VARCHAR);
//执行存储过程
stmt.execute();

String result = stmt.getString(2);//调用存储过程后,返回输出参数
System.out.println(result);

4. 事务入门

1、TPL:事务(Transaction)处理语言
数据库有可能是自动提交事务的(MySQL默认就是自动提交事务的),每一条语句都是单独的事务
start transaction:开启事务
commit:提交事务。永久的存储到硬盘上
rollback:回滚事务。回到最开始的地方

银行转账!张三转10000块到李四的账户,这其实需要两条SQL语句:
	给张三的账户减去10000元;
	给李四的账户加上10000元。
如果在第一条SQL语句执行成功后,在执行第二条SQL语句之前,程序被中断了(可能是抛出了某个异常,也可能是其他什么原因),那么李四的账户没有加上10000元,而张三却减去了10000元。这肯定是不行的!


try {	
	conn = JDBCUtils.getConnection();
	conn.setAutoCommit(false);	//开启事务
	String sql = "update account set money=money+100 where id=1";
	String sql2 = "update account set money=money-100 where id=2";
	stmt = conn.prepareStatement(sql);
	stmt2 = conn.prepareStatement(sql2);
	
	stmt.executeUpdate();
//	int i= 1/0;
	stmt2.executeUpdate();
	conn.commit();
	
}catch(ArithmeticException e) {	
	if(conn != null) {
		try {
			conn.rollback();
		} catch (SQLException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
	}
} 

5. 事务的特性和隔离级别(提高数据安全)

1.      ISOLATION_DEFAULT: 这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别.
	  另外四个与JDBC的隔离级别相对应
2.      ISOLATION_READ_UNCOMMITTED: 这是事务最低的隔离级别,它充许令外一个事务可以看到这个事务未提交的数据。
	  这种隔离级别会产生脏读,不可重复读和幻像读。
3.      ISOLATION_READ_COMMITTED: 保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据
4.      ISOLATION_REPEATABLE_READ: 这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。
5.      ISOLATION_SERIALIZABLE 这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。

	  除了防止脏读,不可重复读外,还避免了幻像读。

1. 脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。
2. 不可重复读是 指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。
那么,在第一个事务中的两次读数据之间,由于第二个事务的修 改,那么第一个事务两次读到的的数据可能是不一样的。
3. 幻觉读是 指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行
。同时,第二个事务也修改这个表中的 数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样

SQL中查询隔离级别
seelect @@session.tx_isolation
seelect @@global.tx_isolation

设置隔离级别
set global transaction isolationlevel
set session transaction isolationlevel 

Java中设置隔离级别
con. setTransactionIsolation(int level)

6. 数据库连接池

当定义了数据库连接池之后,程序需要使用Connection都是从连接池那来获取,但是怎么来获取连接池对象呢?这里又存在一个解耦的问题!
通常在Java Web环境中都会使用JNDI(Java命名目录接口)来查找想要的资源,通常JavaWeb服务器也都是JNDI服务器,所以使用JNDI需要在JavaWeb服务器(Tomcat)中进行配置。
如果需要你的连接池可以配置到JNDI中,那么就要让你的连接池去实现DataSource接口

自定义连接池
	继承原有Connection,然后重写close()方法;
	使用装饰者模式(回忆一下IO流),重写close()方法;(简化代码可以使用适配器模式)
	JDK动态代理

 

7. DBCP

DBCP是Apache提供的一款开源免费的数据库连接池!
Hibernate3.0之后不再对DBCP提供支持!因为Hibernate声明DBCP有致命的缺欠!DBCP因为Hibernate的这一毁谤很是生气,并且说自己没有缺欠

DataSource ds = BasicDataSourceFactory.createDataSource(prop)
ds.getConnection();

8. CP30

1. 导包
	c3p0-0.9.1.2.jar
	mysql-connector-java-5.0.8-bin.jar
2. src路径下c3p0-config.xml
3. 调用
DataSource ds = new ComboPooledDataSource();
return ds.getConnection();

总结:(必须知道)
1、日后尽量使用标准的数据源(一般都带有连接池),为的就是提高效率
2、当用户调用Connection.close()方法,并不能关闭链接,而是还回池中

9. Tomcat配置DBCP连接池

JNDI
	* JNDI:Java Naming and Directory Interface(javax.name.*)
	Tomcat启动时,会按照用户的配置,创建数据源,并把数据源对象绑定在一个名字上
	JNDI就好比window系统的注册表,它是一个Map结构,key是由路径和名称组成的字符串,value就是绑定的一个对象

利用Tomcat管理数据源	
1. 拷贝jar包mysql-connector-java-5.0.8-bin.jar拷贝到tomcat/lib目录
2. 在META-INF目录下建立一个名称为context.xml
	<Context>
	  <Resource name="jdbc/EmployeeDB"
				auth="Container"
				type="javax.sql.DataSource"
				username="root"
				password="root"
				driverClassName="com.mysql.jdbc.Driver"
				url="jdbc:mysql://192.168.13.2:3306/day17"
				maxTotal="8"
				maxIdle="4"/>
	</Context>
3. 不要在main方法获取数据源
4. jsp简单调用
	 <%
	   try {
		   Context initCtx = new InitialContext();
		   Context envCtx = (Context) initCtx.lookup("java:comp/env");
		   DataSource ds = (DataSource) envCtx.lookup("jdbc/EmployeeDB");
			
		   Connection conn = ds.getConnection();
		   System.out.println(conn);
			
		   conn.close();
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
   %>

10. 数据库元信息获取

1. 数据库元数据
	DatabaseMetaData dmd = conn.getMetaData();
	dmd.getDatabaseProductName();//获取数据库产品名称
	dmd.getDatabaseProductVersion();//获取数据库产品版本号

2. 参数元数据
	stmt = conn.prepareStatement("???");
	ParameterMetaData pmd = stmt.getParameterMetaData();//获取参数元信息
	int count = pmd.getParameterCount();//获取参数的个数

3. 结果集元数据
	rs = stmt.executeQuery();
	ResultSetMetaData rsmd = rs.getMetaData();//获取结果集元信息
	int count = rsmd.getColumnCount();//获取结果集信息个数

	for(int i=0; i<count; i++) {
		String columnName = rsmd.getColumnName(i+1);//获取列名
		String columnTypeName = rsmd.getColumnTypeName(i+1);//获取列值
		System.out.println(columnName + ": " + columnTypeName);
	}