Java JDBC的基础知识(五)
本文主要记录JDBC基础知识之后的部分内容。另外,我看到《Java核心基础2》中第四章是主要介绍数据库编程的。里面有一些说明和应用特别灵活,有些部分也太容易理解,建议大家看一下。这篇是依然是剩余部分的基础。
一、事务
事务是一组组合成逻辑工作单元的操作,虽然系统中可能会出错,但事务将控制和维护事务中每个操作的一致性和完整性。
事务具有:
1.原子性(atomicity):组成事务处理的语句形成了一个逻辑单元,不能只执行其中的一部分。
2.一致性(consistency):在事务处理执行前后,数据库是一致的(数据库数据完整性约束)。
3.隔离性(isolcation):一个事务处理对另一个事务处理的影响。
4.持续性(durability):事务处理的效果能够被永久保存下来 。
用一副图来说明一下:
Connection.setAutoCommit(false); //打开事务 Connection.commit(); //揭交事务 Connection.rollback(); //回滚事务
当只想撤销部分操作的时候,可以使用 SavePoint
SavePoint sp=conn.getSavePoint();
conn.rollback(sp);
conn.commit();
代码应用示例:
import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.List; class AdminInfo { public static void main(String[] args) { AdminInfo admin = new AdminInfo(); admin.setId(122); admin.setAddress("杭州文一西路"); admin.setPassword("123"); admin.setUserName("周周"); List<Integer> roleList = new ArrayList<Integer>(); roleList.add(3); roleList.add(4); roleList.add(5); addAdmin(admin, roleList); } private void setId(int i) { // TODO Auto-generated method stub } private void setAddress(String string) { // TODO Auto-generated method stub } private void setPassword(String string) { // TODO Auto-generated method stub } private void setUserName(String string) { // TODO Auto-generated method stub } private String getPassword() { // TODO Auto-generated method stub return null; } private String getUserName() { // TODO Auto-generated method stub return null; } private String getId() { // TODO Auto-generated method stub return null; } public static void addAdmin(AdminInfo admin, List<Integer> roleList) { Connection conn = null; Statement stm = null; try { conn = DBUtil.getConn(); stm = conn.createStatement(); String sql = "insert into adminInfo (id,userName,password) values ( " + admin.getId() + ",'" + admin.getUserName() + "','" + admin.getPassword() + "') "; conn.setAutoCommit(false); // 开启事务 stm.execute(sql); for (Integer i : roleList) { String sql2 = "insert into adminRole(adminid,roleId) values ( " + admin.getId() + "," + i + ")"; @SuppressWarnings("unused") int a = 9 / 0; stm.execute(sql2); } conn.commit(); } catch (Exception ex) { try { conn.rollback(); // 回滚 } catch (SQLException e) { e.printStackTrace(); } ex.printStackTrace(); } finally { DBUtil.close(null, stm, conn); } } }
二、带回滚点的事务
从上面的图中,已经可以了解到,带滚点的事务的特点。只要设置了它,程序因为意外原因而中断,也可以当作数据都回到原来的起始点,就当什么事情都没发生过。
代码操作演示:
import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; import java.sql.Savepoint; import java.util.List; public class Test4 { static void addUserPointDemo(String userName, String userId, List<Integer> roleList) { Connection conn = null; PreparedStatement stm = null; Savepoint point = null; try { conn = DBUtil.getConn(); String sql = "insert into SysUser (userName,userId) values(?,?)"; stm = conn.prepareStatement(sql); stm.setString(1, userName); stm.setString(2, userId); conn.setAutoCommit(false); stm.executeUpdate(); // 下面是添加权限列表 for (int i = 0; i < roleList.size(); i++) { if (i == 3) { // 在等于3的时候设置保存点 point = conn.setSavepoint(); } if (i == 5) { int a = 2 / 0; } String sql2 = "insert into UserRole(userId,roleId) values(?,?)"; stm = conn.prepareStatement(sql2); stm.setString(1, userId); stm.setInt(2, roleList.get(i)); stm.executeUpdate(); } conn.commit(); } catch (Exception e) { // 注意这里的异常类型 try { conn.rollback(point); conn.commit(); // 注意 一定要再commit 一次 throw new RuntimeException(e); } catch (SQLException e1) { throw new RuntimeException(e1.getMessage()); } } } }
三、4 元信息
DatabaseMetaData
ParameterMetaData
ResultSetMetaData
1) DatabaseMetaData
getURL():返回一个String类对象,代表数据库的URL。 //jdbc:mysql://localhost:3306/shop getUserName():返回连接当前数据库管理系统的用户名。 //root@localhost getDatabaseProductName():返回数据库的产品名称。 //MySQL getDatabaseProductVersion():返回数据库的版本号。 getDriverName():返回驱动驱动程序的名称。 //MySQL-AB JDBC Driver getDriverVersion():返回驱动程序的版本号。 //mysql-connector-java-5.0.4 ( $Date: 2006-10-19 17:47:48 +0200 (Thu, 19 Oct 2006) $, $Revision: 5908 $ ) isReadOnly():返回一个boolean值,指示数据库是否只允许读操作。//false supportsTransactions() //true getSQLKeywords() //AUTO_INCREMENT,BINARY,BLOB,ENUM,INFILE,LOAD,MEDIUMINT,OPTION,OUTFILE,REPLACE,SET,TEXT,UNSIGNED,ZEROFILL
2) ParameterMetaData(stm.getParameterMetaData();)
getParameterCount(): 获得指定参数的个数 getParameterType(int param):获得指定参数的sql类型 //mysql不支持
代码演示:
import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; public class Test5 { public static AdminInfo login(String sql, Object[] paramList) { Connection conn = null; PreparedStatement stm = null; ResultSet rs = null; AdminInfo admin = null; try { conn = DBUtil.getConn(); stm = conn.prepareStatement(sql); /* * ParameterMetaData pm=stm.getParameterMetaData(); int * paramCount=pm.getParameterCount(); * System.out.println("该sql一共要"+paramCount+"个参数"); */ for (int i = 1; i <= paramList.length; i++) { stm.setObject(i, paramList[i - 1]); } rs = stm.executeQuery(); if (rs.next()) { admin = new AdminInfo(); admin.setId(rs.getInt("id")); admin.setUserName(rs.getString("userName")); admin.setPassword(rs.getString("password")); // ... } } catch (Exception ex) { ex.printStackTrace(); } finally { DBUtil.close(null, stm, conn); } return admin; } }
3) ResultSetMeta (resultSet 的元信息)
getColumnCount() //得到列数 getColumnName(int colum) //得到指定列的列名 真名 getColumnLabel(int colum) //得到指定列的列名 as 后的 getColumnTypeName (int column) //得到指定列的类型
代码演示:
import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.ResultSetMetaData; public class Test6 { public static AdminInfo test() { Connection conn = null; PreparedStatement stm = null; ResultSet rs = null; AdminInfo admin = null; try { conn = DBUtil.getConn(); stm = conn .prepareStatement("SELECT * FROM ADMININFO where username ='赵明明' and password='123'"); rs = stm.executeQuery(); if (rs.next()) { ResultSetMetaData rm = rs.getMetaData(); int columnCount = rm.getColumnCount(); // 取列数 for (int i = 1; i <= columnCount; i++) { System.out.println(rm.getColumnLabel(i)); // 取出所有的列名 } admin = new AdminInfo(); admin.setId(rs.getInt("id")); admin.setUserName(rs.getString("userName")); admin.setPassword(rs.getString("password")); } } catch (Exception ex) { ex.printStackTrace(); } finally { DBUtil.close(null, stm, conn); } return admin; } }
四、数据源和连接池
DataSource 用来取代 DriverManager 来取得Connection 。通过 DataSource 取得连接的速度很快。通过 DataSource 方式取得的连接(Connection ) 是经过代理的,它的close方法,不会导致原来的连接的关闭。一般的 DataSource 内部都有一个连接池来缓存 Connection,可以大大提高效率。这个连接池一般就是 一个 Collection 集合。
例子:简单的数据源模拟
//数据源 public class MyDataSource { private String url="jdbc:mysql://localhost:3306/shop"; private String user="root"; private String password="admin"; private LinkedList<Connection> connPool=new LinkedList<Connection>(); public MyDataSource(){ for(int i=0;i<10;i++){ this.connPool.addLast(this.createConn()); } } //对外提供取得连接的方法 public Connection getConn(){ return this.connPool.removeFirst(); } //对外提供收回连接的方法 public void closeConn(Connection conn){ this.connPool.addLast(conn); } //创建连接 private Connection createConn() { Connection conn=null; try { conn= DriverManager.getConnection(url,user,password); } catch (SQLException e) { e.printStackTrace(); } return conn; } }
//在DBUtil中使用自定义的数据源 public class DBUtil { private DBUtil() {} private static MyDataSource myDataSorce=null; //数据源对象 private static String className="com.mysql.jdbc.Driver"; static { try { Class.forName(className); myDataSorce=new MyDataSource(); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } } // 得到连接 public static Connection getConn() { return myDataSorce.getConn(); } // 关闭连接 public static void close(ResultSet rs, Statement stm, Connection conn) { if (rs != null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if (stm != null) { try { stm.close(); } catch (SQLException e) { e.printStackTrace(); } } if (conn != null) { myDataSorce.closeConn(conn); //关连接的方式也换掉了 } } }
数据源产品的使用
dbcp DataSource
1) 导入jar 包
commons-collections-3.1.jar
commons-dbcp-1.2.2.jar
commons-pool.jar
配置文件
dbcpconfig.properties
2) 修改配置文件
#连接设置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/shop
username=root
password=admin
#<!-- 初始化连接 -->
initialSize=10
#最大连接数量
maxActive=50
#<!-- 最大空闲连接 -->
maxIdle=20
#<!-- 最小空闲连接 -->
minIdle=5
#<!-- 超时等待时间以毫秒为单位 6000毫秒/1000等于60秒 -->
maxWait=60000
#JDBC驱动建立连接时附带的连接属性属性的格式必须为这样:[属性名=property;]
#注意:"user" 与 "password" 两个属性会被明确地传递,因此这里不需要包含他们。
#connectionProperties=useUnicode=true;characterEncoding=gbk
#指定由连接池所创建的连接的自动提交(auto-commit)状态。
#defaultAutoCommit=true
#driver default 指定由连接池所创建的连接的只读(read-only)状态。
#如果没有设置该值,则“setReadOnly”方法将不被调用。(某些驱动并不支持只读模式,如:Informix)
defaultReadOnly=
#driver default 指定由连接池所创建的连接的事务级别(TransactionIsolation)。
#可用值为下列之一:(详情可见javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE
defaultTransactionIsolation=READ_UNCOMMITTED
import java.io.InputStream; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Properties; import javax.sql.DataSource; import org.apache.commons.dbcp.BasicDataSourceFactory; public class DBUtil { private DBUtil() {} private static DataSource dataSource = null; // 数据源对象 static { try { // 读dbcp的配置文件 Properties settings = new Properties(); InputStream in = DBUtil.class.getClassLoader().getResourceAsStream( "dbcpconfig.properties"); settings.load(in); dataSource=BasicDataSourceFactory.createDataSource(settings); } catch (Exception ex) { ex.printStackTrace(); } } // 得到连接 public static Connection getConn() { Connection conn= null; try { conn= dataSource.getConnection(); //返回的是一个代理对象 } catch (SQLException e) { e.printStackTrace(); } return conn; } // 关闭连接 public static void close(ResultSet rs, Statement stm, Connection conn) { if (rs != null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if (stm != null) { try { stm.close(); } catch (SQLException e) { e.printStackTrace(); } } if (conn != null) { try { conn.close(); //由于 conn 现在是个代理对象,所以它的close方法并不会真正的关掉连接,而是把它放回边接池中 } catch (SQLException e) { e.printStackTrace(); } } } }