JDBC高级特性
1.如何使用JDBC事务
(1)事务的四个特性:
一.原子性(Atomicity)
原子性是指事务是一个不可分割的单位,事务中的操作要么都发生,要么都不发生。
二.一致性(Consistency)
事务必须使数据库从一个一致性状态变换到另外一个一致性状态。(数据不被破坏)
三.隔离性(Isolation)
事务的隔离性是指一个事务的执行不被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
四.持久性(Durability)
持久性是指一个事务一旦被提交,它对数据库中的数据的改变就是永久性的,接下来的其操作和数据库故障不应该对其有任何影响。
在JDBC中,事务默认是自动提交的,每次执行一个SQL语句时,如果执行成功,就会向数据库自动提交,而不能回滚。
(2)取消JDBC默认的自动提交方式,改为手动提交
conn.setAutoCommit(false);
(3)多有JDBC操作都执行完毕后提交事务
conn.commit();
(4)有一个JDBC操作发生问题就回滚事务
conn.rollback();
(5)可以通过设置事务的保存点来灵活的进行控制(Savepoint).
Savepoint是java.sql包的一个接口,可以在回滚事务时引用当前事务中的保存点,将事务回滚到保存点,在该保存点之后所做的全部操作都将被撤销。
conn.setSavepoint();
2.JDBC批量操作
1 package cn.com.demo; 2 import java.io.BufferedReader; 3 import java.io.File; 4 import java.io.FileReader; 5 import java.sql.Connection; 6 import java.sql.PreparedStatement; 7 import java.sql.SQLException; 8 import java.sql.Timestamp; 9 import java.util.ArrayList; 10 import java.util.Iterator; 11 import java.util.List; 12 import java.util.regex.Pattern; 13 public class TestBatch { 14 private Connection conn = null; 15 private PreparedStatement pstmt = null; 16 public static void main(String[] args) { 17 TestBatch tb = new TestBatch(); 18 List<String> list = tb.readFile(); 19 Iterator<String> iter = list.iterator(); 20 while (iter.hasNext()) { 21 System.out.println(iter.next() + "\t"); 22 } 23 tb.updateSms(list); 24 } 25 public void updateSms(List<String> list) { 26 int i = 0; 27 long startTime = System.currentTimeMillis(); 28 try { 29 String sql = "insert into sendsms(name,sourcetel,desttel,senddate) values(?,?,?,?)"; 30 conn = DaoFactory.getConnection(); 31 conn.setAutoCommit(false);// 设置事务手动提交 32 pstmt = conn.prepareStatement(sql); 33 for (String tel : list) { // 循环获取手机号 34 pstmt.setString(1, "张苏纳"); 35 pstmt.setString(2, "15026470454"); 36 pstmt.setString(3, tel); 37 pstmt.setTimestamp(4, 38 new Timestamp(new java.util.Date().getTime())); 39 pstmt.addBatch();// 加入缓存 40 i++; 41 if (i % 10000 == 0) {// 10000保存一次 42 pstmt.executeBatch();// 批量处理 43 pstmt.clearBatch();// 清空pstmt中积攒的sql 44 } 45 // pstmt.executeUpdate();// 执行保存 46 } 47 pstmt.executeBatch();// 执行所有的sql语句 48 conn.commit(); // 提交事务 49 } catch (Exception e) { 50 try { 51 conn.rollback(); 52 } catch (SQLException e1) { 53 e1.printStackTrace(); 54 } 55 e.printStackTrace(); 56 } finally { 57 DaoFactory.closeAll(null, pstmt, conn); 58 } 59 long endTime = System.currentTimeMillis(); 60 long time = (endTime - startTime) / 1000; 61 System.out.println("耗时" + time + "秒。"); 62 } 63 // 读取号码文件 64 public List<String> readFile() { 65 List<String> list = new ArrayList<String>(); 66 File file = new File("E:" + File.separator + "测试.txt"); // 100000个手机号 67 try { 68 FileReader fileReader = new FileReader(file); 69 BufferedReader bufferedReader = new BufferedReader(fileReader); 70 while (bufferedReader.ready()) { 71 String tel = bufferedReader.readLine(); 72 if (tel.length() > 0) { 73 if (tel.contains(" ")) { 74 tel = tel.replace(" ", ""); 75 } 76 // 验证手机号 77 if (!Pattern.compile("^1[358]\\d{9}$").matcher(tel) 78 .matches()) { 79 System.out.println("手机号码格式不正确" + tel); 80 continue; 81 } 82 list.add(tel); 83 } 84 } 85 bufferedReader.close(); 86 fileReader.close(); 87 } catch (Exception e) { 88 e.printStackTrace(); 89 e.getMessage(); 90 } 91 return list; 92 } 93 }
3.DAO模式(即数据存取对象)
使用DAO模式进行项目开发主要由以下两个好处:
(1)隔离了业务逻辑代码和数据访问代码,分工明确,降低耦合度,提高可重用性
(2)采用面向接口编程,提高了项目的可扩展和可维护性。
一个典型的DAO模式主要由一下几个部分组成:
(1)一个工厂类,主要负责创建和关闭Connection对象
(2)DAO接口,定义业务方法
(3)实体类,储存和传递数据
4.DAO模式扩展
package cn.com.dao; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.sql.Connection; import java.sql.Date; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Statement; import java.sql.Types; import java.util.ArrayList; import java.util.List; public class BaseDAO<T> { private final String driver = "com.mysql.jdbc.Driver"; private final String url = "jdbc:mysql://localhost:3306/mytest"; private final String user = "root"; private final String password = ""; // 获取连接 public Connection getConnection() { Connection conn = null; try { Class.forName(driver); conn = DriverManager.getConnection(url, user, password); } catch (Exception e) { e.printStackTrace(); } return conn; } // 执行增删改的方法 public int executeUpdate(String sql, Object[] params) { Connection conn = null; PreparedStatement pstmt = null; int count = 0; try { conn = this.getConnection(); pstmt = conn.prepareStatement(sql); setParams(pstmt, params); count = pstmt.executeUpdate(); } catch (Exception e) { e.printStackTrace(); } finally { this.closeAll(null, pstmt, conn); } return count; } /** * 泛型查询 * * @param sql * SQL语句 * @param args * 查询语句的参数 * @param className * 类的完全限定名 * @return */ @SuppressWarnings("unchecked") public List<T> generalSearch(String sql, Object[] args, String className) { // 存储返回的集合对象 List<T> list = new ArrayList<T>(); Connection conn = this.getConnection(); PreparedStatement pstmt = null; ResultSet rs = null; try { // 使用反射获取类的相关信息 Class<T> clazz = (Class<T>) Class.forName(className); Field[] fields = clazz.getDeclaredFields(); Method method = null; pstmt = conn.prepareStatement(sql); setParams(pstmt, args); rs = pstmt.executeQuery(); ResultSetMetaData resmd = null; // 获取结果集的元素据 resmd = rs.getMetaData(); // 获取一共多少列 int columnCount = resmd.getColumnCount(); while (rs.next()) { T obj = clazz.newInstance(); // 创建一个新对象 for (int i = 1; i <= columnCount; i++) { String cname = resmd.getColumnName(i); // 获取每一列的名称 int ctype = resmd.getColumnType(i);// 获取每一列的类型 for (Field field : fields) { // 循环实体类的列 if (cname.equals(field.getName())) { // 如果列名和属性名相同 // 封装数据 String firstLetter = field.getName() .substring(0, 1).toUpperCase(); String methodName = "set" + firstLetter + field.getName().substring(1); // 如果列时整型的 if (ctype == Types.INTEGER) { method = clazz.getMethod(methodName, Integer.class); method.invoke(obj, rs.getInt(i)); } else if (ctype == Types.VARCHAR) { method = clazz.getMethod(methodName, String.class); method.invoke(obj, rs.getString(i)); } else if (ctype == Types.TIMESTAMP) { method = clazz .getMethod(methodName, Date.class); method.invoke(obj, rs.getDate(i)); } else if (ctype == Types.NUMERIC) { method = clazz.getMethod(methodName, Double.class); method.invoke(obj, rs.getDouble(i)); } } } } list.add(obj); } } catch (Exception e) { e.printStackTrace(); } finally { this.closeAll(rs, pstmt, conn); } return list; } // 设置参数的方法 public void setParams(PreparedStatement ps, Object[] params) { if (params == null) { return; } for (int i = 0; i < params.length; i++) { try { ps.setObject(i + 1, params[i]); } catch (SQLException e) { e.printStackTrace(); } } } // 释放资源 public void closeAll(ResultSet rs, Statement stmt, Connection conn) { try { if (rs != null) rs.close(); if (stmt != null) stmt.close(); if (conn != null) conn.close(); } catch (Exception e) { e.printStackTrace(); } } }
5.JDBC元数据
JDBC到了版本3以后加入了元数据的功能,有了JDB元数据就可以很方便的获取数据库对相关的信息。
1 package cn.com.demo; 2 3 import java.sql.Connection; 4 import java.sql.PreparedStatement; 5 import java.sql.ResultSet; 6 import java.sql.SQLException; 7 8 import com.mysql.jdbc.DatabaseMetaData; 9 import com.mysql.jdbc.ResultSetMetaData; 10 11 public class MetaDataTest { 12 public static void main(String[] args) { 13 testDatabaseMetaData(); 14 String sql = "select * from userinfo"; 15 testResultSetMetaData(sql); 16 } 17 18 /** 19 * DatabaseMetaData接口可以获取关于数据库的整体综合信息,譬如数据库产品的名称等等。 20 */ 21 public static void testDatabaseMetaData() { 22 BaseDAO base = new BaseDAO(); 23 Connection conn = base.getConnection(); 24 try { 25 DatabaseMetaData dmd = (DatabaseMetaData) conn.getMetaData(); 26 String pname = dmd.getDatabaseProductName();// 获取数据库产品名称 27 String driverName = dmd.getDriverName();// 获取驱动名称 28 String pversion = dmd.getDatabaseProductVersion();// 获取驱动版本号 29 int isolation = dmd.getDefaultTransactionIsolation();// 获取默认隔离级别 30 System.out.println("数据库产品:" + "\n" + pname + "驱动名称:" + "\n" 31 + driverName + "\n" + "驱动版本:" + "\n" + pversion + "隔离级别:" 32 + isolation); 33 } catch (SQLException e) { 34 e.printStackTrace(); 35 } 36 } 37 38 /** 39 * ResultSetMetaData接口可以获取关于ResultSet对象中列的类型和属性的信息。 40 * 因为结果集是从数据库表中查询到的一个二维表,所以可以获得这个表格的列的数量,列的类型,列的名称等信息。 41 * 42 * @param sql 43 */ 44 public static void testResultSetMetaData(String sql) { 45 BaseDAO base = new BaseDAO(); 46 Connection conn = base.getConnection(); 47 PreparedStatement pstmt = null; 48 ResultSet rs = null; 49 try { 50 pstmt = conn.prepareStatement(sql); 51 rs = pstmt.executeQuery(); 52 ResultSetMetaData resmd = null; 53 resmd = (ResultSetMetaData) rs.getMetaData();// 获取结果集的元数据 54 int columnCount = resmd.getColumnCount();// 获取一共有多少列 55 for (int i = 0; i < columnCount; i++) { 56 String cname = resmd.getColumnName(i);// 获取每一列的名称 57 int ctype = resmd.getColumnType(i);// 获取每一列的类型 58 String tname = resmd.getColumnTypeName(i);// 获取类型名称 59 System.out.println("列明:" + cname + "\t列类型名称:" + tname 60 + "\t列类型:" + ctype); 61 } 62 } catch (SQLException e) { 63 e.printStackTrace(); 64 } finally { 65 DaoFactory.closeAll(rs, pstmt, conn); 66 } 67 } 68 69 }
数据库连接
1 package cn.com.util; 2 3 import java.sql.Connection; 4 import java.sql.DriverManager; 5 import java.sql.ResultSet; 6 import java.sql.Statement; 7 8 public class DaoFactory { 9 private static String driver = "oracle.jdbc.driver.OracleDriver"; 10 private static String url = "jdbc:oracle:thin:@localhost:1521:orcl"; 11 private static String user = "scott"; 12 private static String password = "tiger"; 13 14 public static Connection getConnection() { 15 Connection conn = null; 16 try { 17 Class.forName(driver); 18 conn = DriverManager.getConnection(url, user, password); 19 } catch (Exception e) { 20 e.printStackTrace(); 21 } 22 return conn; 23 } 24 25 public static void closeAll(ResultSet rs, Statement stmt, Connection conn) { 26 try { 27 if (rs != null) 28 rs.close(); 29 if (stmt != null) 30 stmt.close(); 31 if (conn != null) 32 conn.close(); 33 } catch (Exception e) { 34 e.printStackTrace(); 35 } 36 } 37 }
6.读写二进制文件
1 package cn.com.util; 2 3 import java.io.File; 4 import java.io.FileInputStream; 5 import java.io.FileOutputStream; 6 import java.io.InputStream; 7 import java.sql.Blob; 8 import java.sql.Connection; 9 import java.sql.PreparedStatement; 10 import java.sql.ResultSet; 11 12 /** 13 * 读写二进制文件 14 * 15 * @author wangxiaocong 16 * 17 */ 18 public class BlobTest { 19 public static void main(String[] args) { 20 //writeBlob(); 21 readerBlob(); 22 } 23 24 // 写入二进制文件 25 public static void writeBlob() { 26 InputStream is = null; 27 Connection conn = null; 28 PreparedStatement pstmt = null; 29 try { 30 conn = DaoFactory.getConnection(); 31 String sql = "update stuinfo set file_data = ?,file_name = ? where id = 3 "; 32 pstmt = conn.prepareStatement(sql); 33 File f = new File("D:" + File.separator + "蓝波 - 迎着风.mp3"); 34 is = new FileInputStream(f); 35 // 设置二进制流参数 36 pstmt.setBinaryStream(1, is, f.length()); 37 pstmt.setString(2, f.getName()); 38 int result = pstmt.executeUpdate(); 39 if (result == 1) { 40 System.out.println("添加音乐成功"); 41 } 42 } catch (Exception e) { 43 e.printStackTrace(); 44 } finally { 45 DaoFactory.closeAll(null, pstmt, conn); 46 } 47 } 48 49 // 读取二进制文件 50 public static void readerBlob() { 51 Connection conn = null; 52 PreparedStatement pstmt = null; 53 ResultSet rs = null; 54 FileOutputStream out = null; 55 try { 56 conn = DaoFactory.getConnection(); 57 String sql = "select file_name,file_data from stuinfo where id = 3"; 58 pstmt = conn.prepareStatement(sql); 59 rs = pstmt.executeQuery(); 60 rs.next(); 61 String fileName = rs.getString(1); 62 System.out.println(fileName); 63 Blob blob = rs.getBlob(2); 64 out = new FileOutputStream("D:" + File.separator 65 + new File(fileName).getName()); 66 out.write(blob.getBytes(1 67 , (int) blob.length())); 68 out.close(); 69 String[] cmdStrings = { "F:\\酷狗音乐\\PPStream\\GeePlayer\\3.0.40.3591\\GeePlayer.exe", 70 "D:" + File.separator + fileName }; 71 Runtime.getRuntime().exec(cmdStrings);// 打开播放器 72 } catch (Exception e) { 73 e.printStackTrace(); 74 } finally { 75 DaoFactory.closeAll(rs, pstmt, conn); 76 } 77 } 78 }
7.读写大段文本
1 package cn.com.util; 2 3 import java.io.BufferedReader; 4 import java.io.File; 5 import java.io.FileReader; 6 import java.io.FileWriter; 7 import java.io.Reader; 8 import java.sql.Connection; 9 import java.sql.PreparedStatement; 10 import java.sql.ResultSet; 11 /** 12 * 读写大段文本 13 * @author wangxiaocong 14 * 15 */ 16 public class ClobTest { 17 private static Connection conn = null; 18 private static PreparedStatement pstmt = null; 19 private static ResultSet rs = null; 20 21 public static void main(String[] args) { 22 //writeText(); 23 readText(); 24 } 25 26 // 写入大文本 27 public static void writeText() { 28 try { 29 conn = DaoFactory.getConnection(); 30 String sql = "update stuinfo set resume = ? where id = 3"; 31 pstmt = conn.prepareStatement(sql); 32 File file = new File("src/resume.txt"); 33 Reader reader = new BufferedReader(new FileReader(file)); 34 pstmt.setCharacterStream(1, reader); 35 int i = pstmt.executeUpdate(); 36 reader.close(); 37 System.out.println(i); 38 } catch (Exception e) { 39 e.printStackTrace(); 40 } finally { 41 DaoFactory.closeAll(rs, pstmt, conn); 42 } 43 } 44 45 // 读取大文本 46 public static void readText() { 47 try { 48 conn = DaoFactory.getConnection(); 49 String sql = "select * from stuinfo where id = 3"; 50 pstmt = conn.prepareStatement(sql); 51 rs = pstmt.executeQuery(); 52 if (rs.next()) { 53 Reader reader = rs.getCharacterStream("resume"); 54 char buff[] = new char[1024]; 55 int len = 0; 56 FileWriter writer = new FileWriter("D:"+File.separator+"resume.txt"); 57 while ((len = reader.read(buff)) > 0) { 58 System.out.println(new String(buff, 0, len)); 59 writer.write(buff, 0, len); 60 } 61 writer.close(); 62 reader.close(); 63 } 64 } catch (Exception e) { 65 e.printStackTrace(); 66 } finally { 67 DaoFactory.closeAll(rs, pstmt, conn); 68 } 69 } 70 }