一、JDBC基础
连接数据的步骤:
1.注册驱动 :Class.forName(“com.mysql.jdbc.Driver”) 推荐这种方式,不会对具体的驱动类产生依赖;DriverManager.registerDriver(com.mysql.jdbc.Driver)
会造成DriverManager中产生两个一样的驱动,并会对具体的驱动类产生依赖;System.setProperty(“jdbc.drivers”, “driver1:driver2”) 虽然不会对具体的驱动类产生依赖;但注册不太方便,所以很少使用。
2.建立连接(Connection) :Connection conn = DriverManager.getConnection(url, user, password);url格式: JDBC:子协议:子名称//主机名:端口/数据库名?属性名=属性值&...;User,password可以用“属性名=属性值”方式告诉数据库;其他参数如:useUnicode=true&characterEncoding=GBK。
3.创建执行SQL的语句(Statement):
4.执行语句
5.处理执行结果(ResultSet)
6.释放资源
1、注册数据库驱动的方式:
1)加载 JDBC 驱动需调用 Class 类的静态方法 forName(),向其传递要加载的 JDBC 驱动的类名;
1 @Test 2 public void testDriverManager() throws Exception{ 3 //1. 准备连接数据库的 4 个字符串. 4 //驱动的全类名. 5 String driverClass = "com.mysql.jdbc.Driver"; 6 //JDBC URL 7 String jdbcUrl = "jdbc:mysql://localhost:3306/test"; 8 //user 9 String user = "root"; 10 //password 11 String password = "123456"; 12 13 //2. 加载数据库驱动程序(对应的 Driver 实现类中有注册驱动的静态代码块.) 14 Class.forName(driverClass); 15 16 //3. 通过 DriverManager 的 getConnection() 方法获取数据库连接. 17 Connection connection = 18 DriverManager.getConnection(jdbcUrl, user, password); 19 System.out.println(connection); 20 21 }
2)Driver 是一个接口: 数据库厂商必须提供实现的接口. 能从其中获取数据库连接.可以通过 Driver 的实现类对象获取数据库连接.
1 @Test 2 public void testDriver() throws SQLException { 3 //1. 创建一个 Driver 实现类的对象 4 Driver driver = new com.mysql.jdbc.Driver(); 5 6 //2. 准备连接数据库的基本信息: url, user, password 7 String url = "jdbc:mysql://localhost:3306/test"; 8 Properties info = new Properties(); 9 info.put("user", "root"); 10 info.put("password", "123456"); 11 12 //3. 调用 Driver 接口的 connect(url, info) 获取数据库连接 13 Connection connection = driver.connect(url, info); 14 System.out.println(connection); 15 }
2、获取数据库连接的方式:
1)DriverManager 是驱动的管理类:1). 可以通过重载的 getConnection() 方法获取数据库连接. 较为方便,2). 可以同时管理多个驱动程序: 若注册了多个数据库连接, 则调用 getConnection(),3)方法时传入的参数不同, 即返回不同的数据库连接。
例:Connection connection = DriverManager.getConnection(jdbcUrl, user, password);
2)Driver 是一个接口: 数据库厂商必须提供实现的接口. 能从其中获取数据库连接.可以通过 Driver 的实现类对象获取数据库连接.
例:Connection connection = driver.connect(url, info);
3.创建执行SQL的语句(statement、preparedstatement):
通过 JDBC 向指定的数据表中插入一条记录.
a. Statement: 用于执行 SQL 语句的对象
1). 通过 Connection 的 createStatement() 方法来获取
2). 通过 executeUpdate(sql) 可以执行 SQL 语句.
3). 传入的 SQL 可以是 INSRET, UPDATE 或 DELETE. 但不能是 SELECT
b. Connection、Statement 都是应用程序和数据库服务器的连接资源. 使用后一定要关闭.
需要在 finally 中关闭 Connection 和 Statement 对象.
c. 关闭的顺序是: 先关闭后获取的. 即先关闭 Statement 后关闭 Connection
示例代码如下:
1 @Test 2 public void testStatement() throws Exception{ 3 //1. 获取数据库连接 4 Connection conn = null; 5 Statement statement = null; 6 7 try { 8 conn = JDBCTools.getConnection(); 9 10 //3. 准备插入的 SQL 语句 11 String sql = null; 12 13 //sql = "INSERT INTO customers (NAME, EMAIL, BIRTH) " + 14 //"VALUES(‘XYZ‘, ‘[email protected]‘, ‘1990-12-12‘)"; 15 //sql = "DELETE FROM customers WHERE id = 1"; 16 sql = "UPDATE customers SET name = ‘TOM‘ " + 17 "WHERE id = 4"; 18 System.out.println(sql); 19 20 //4. 执行插入. 21 //1). 获取操作 SQL 语句的 Statement 对象: 22 //调用 Connection 的 createStatement() 方法来获取 23 statement = conn.createStatement(); 24 25 //2). 调用 Statement 对象的 executeUpdate(sql) 执行 SQL 语句进行插入 26 statement.executeUpdate(sql); 27 } catch (Exception e) { 28 e.printStackTrace(); 29 } finally{ 30 JDBCTools.release(statement,conn); 31 }
4.处理执行结果(ResultSet):
ResultSet: 结果集. 封装了使用 JDBC 进行查询的结果.
a. 调用 Statement 对象的 executeQuery(sql) 可以得到结果集.
b. ResultSet 返回的实际上就是一张数据表. 有一个指针指向数据表的第一样的前面.可以调用 next() 方法检测下一行是否有效. 若有效该方法返回 true, 且指针下移. 相当于Iterator 对象的 hasNext() 和 next() 方法的结合体
c. 当指针对位到一行时, 可以通过调用 getXxx(index) 或 getXxx(columnName),获取每一列的值. 例如: getInt(1), getString("name")
d. ResultSet 当然也需要进行关闭.
示例代码如下:
1 @Test 2 public void testResultSet(){ 3 //获取 id=4 的 customers 数据表的记录, 并打印 4 5 Connection conn = null; 6 Statement statement = null; 7 ResultSet rs = null; 8 9 try { 10 //1. 获取 Connection 11 conn = JDBCTools.getConnection(); 12 System.out.println(conn); 13 14 //2. 获取 Statement 15 statement = conn.createStatement(); 16 System.out.println(statement); 17 18 //3. 准备 SQL 19 String sql = "SELECT id, name, email, birth " + 20 "FROM customers"; 21 22 //4. 执行查询, 得到 ResultSet 23 rs = statement.executeQuery(sql); 24 System.out.println(rs); 25 26 //5. 处理 ResultSet 27 while(rs.next()){ 28 int id = rs.getInt(1); 29 String name = rs.getString("name"); 30 String email = rs.getString(3); 31 Date birth = rs.getDate(4); 32 33 System.out.println(id); 34 System.out.println(name); 35 System.out.println(email); 36 System.out.println(birth); 37 } 38 39 } catch (Exception e) { 40 e.printStackTrace(); 41 } finally{ 42 //6. 关闭数据库资源. 43 JDBCTools.release(rs, statement, conn); 44 } 45 46 }
JDBC工具模板(JDBCTools)配置如下:
1 import java.io.InputStream; 2 import java.sql.Connection; 3 import java.sql.DriverManager; 4 import java.sql.ResultSet; 5 import java.sql.SQLException; 6 import java.sql.Statement; 7 import java.util.Properties; 8 9 /** 10 * 操作 JDBC 的工具类. 其中封装了一些工具方法 Version 1 11 */ 12 public class JDBCTools { 13 14 public static void release(ResultSet rs, 15 Statement statement, Connection conn) { 16 if(rs != null){ 17 try { 18 rs.close(); 19 } catch (SQLException e) { 20 e.printStackTrace(); 21 } 22 } 23 24 25 if (statement != null) { 26 try { 27 statement.close(); 28 } catch (Exception e2) { 29 e2.printStackTrace(); 30 } 31 } 32 33 if (conn != null) { 34 try { 35 conn.close(); 36 } catch (Exception e2) { 37 e2.printStackTrace(); 38 } 39 } 40 } 41 42 /** 43 * 关闭 Statement 和 Connection 44 * @param statement 45 * @param conn 46 */ 47 public static void release(Statement statement, Connection conn) { 48 if (statement != null) { 49 try { 50 statement.close(); 51 } catch (Exception e2) { 52 e2.printStackTrace(); 53 } 54 } 55 56 if (conn != null) { 57 try { 58 conn.close(); 59 } catch (Exception e2) { 60 e2.printStackTrace(); 61 } 62 } 63 } 64 65 /** 66 * 1. 获取连接的方法. 通过读取配置文件从数据库服务器获取一个连接. 67 * 68 * @return 69 * @throws Exception 70 */ 71 public static Connection getConnection() throws Exception { 72 // 1. 准备连接数据库的 4 个字符串. 73 // 1). 创建 Properties 对象 74 Properties properties = new Properties(); 75 76 // 2). 获取 jdbc.properties 对应的输入流 77 InputStream in = JDBCTools.class.getClassLoader().getResourceAsStream( 78 "jdbc.properties"); 79 80 // 3). 加载 2) 对应的输入流 81 properties.load(in); 82 83 // 4). 具体决定 user, password 等4 个字符串. 84 String user = properties.getProperty("user"); 85 String password = properties.getProperty("password"); 86 String jdbcUrl = properties.getProperty("jdbcUrl"); 87 String driver = properties.getProperty("driver"); 88 89 // 2. 加载数据库驱动程序(对应的 Driver 实现类中有注册驱动的静态代码块.) 90 Class.forName(driver); 91 92 // 3. 通过 DriverManager 的 getConnection() 方法获取数据库连接. 93 return DriverManager.getConnection(jdbcUrl, user, password); 94 } 95 96 }
1 driver=com.mysql.jdbc.Driver 2 jdbcUrl=jdbc:mysql://localhost:3306/test 3 user=root 4 password=123456
二、实现数据库增删改查
1.创立数据库表 examstudent;
1 /* 2 Navicat MySQL Data Transfer 3 4 Source Server : localhost 5 Source Server Version : 50524 6 Source Host : localhost:3306 7 Source Database : examstudent 8 9 Target Server Type : MYSQL 10 Target Server Version : 50524 11 File Encoding : 65001 12 13 Date: 2015-06-27 15:49:22 14 */ 15 16 SET FOREIGN_KEY_CHECKS=0; 17 18 -- ---------------------------- 19 -- Table structure for examstudent 20 -- ---------------------------- 21 DROP TABLE IF EXISTS `examstudent`; 22 CREATE TABLE `examstudent` ( 23 `flowid` int(11) NOT NULL, 24 `type` int(11) DEFAULT NULL, 25 `idcard` varchar(18) DEFAULT NULL, 26 `examcard` varchar(15) DEFAULT NULL, 27 `studentname` varchar(20) DEFAULT NULL, 28 `location` varchar(20) DEFAULT NULL, 29 `grade` int(11) DEFAULT NULL, 30 PRIMARY KEY (`flowid`) 31 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
2.向数据库中添加如下数据
3. 在 eclipse 中建立 java 程序:输入身份证号或准考证号可以查询到学生的基本信息。
4.完成学生信息的删除功能
示例代码如下:
jdbc.properties
1 user=root 2 password=123456 3 driverClass=com.mysql.jdbc.Driver 4 url=jdbc:mysql://localhost:3306/examstudent
student.java
1 package com.atguigu.jdbc; 2 3 public class Student { 4 5 // 流水号 6 private int flowId; 7 // 考试的类型 8 private int type; 9 // 身份证号 10 private String idCard; 11 // 准考证号 12 private String examCard; 13 // 学生名 14 private String studentName; 15 // 学生地址 16 private String location; 17 // 考试分数. 18 private int grade; 19 20 public int getFlowId() { 21 return flowId; 22 } 23 24 public void setFlowId(int flowId) { 25 this.flowId = flowId; 26 } 27 28 public int getType() { 29 return type; 30 } 31 32 public void setType(int type) { 33 this.type = type; 34 } 35 36 public String getIdCard() { 37 return idCard; 38 } 39 40 public void setIdCard(String idCard) { 41 this.idCard = idCard; 42 } 43 44 public String getExamCard() { 45 return examCard; 46 } 47 48 public void setExamCard(String examCard) { 49 this.examCard = examCard; 50 } 51 52 public String getStudentName() { 53 return studentName; 54 } 55 56 public void setStudentName(String studentName) { 57 this.studentName = studentName; 58 } 59 60 public String getLocation() { 61 return location; 62 } 63 64 public void setLocation(String location) { 65 this.location = location; 66 } 67 68 public int getGrade() { 69 return grade; 70 } 71 72 public void setGrade(int grade) { 73 this.grade = grade; 74 } 75 76 public Student(int flowId, int type, String idCard, String examCard, 77 String studentName, String location, int grade) { 78 super(); 79 this.flowId = flowId; 80 this.type = type; 81 this.idCard = idCard; 82 this.examCard = examCard; 83 this.studentName = studentName; 84 this.location = location; 85 this.grade = grade; 86 } 87 88 public Student() { 89 // TODO Auto-generated constructor stub 90 } 91 92 @Override 93 public String toString() { 94 return "Student [flowId=" + flowId + ", type=" + type + ", idCard=" 95 + idCard + ", examCard=" + examCard + ", studentName=" 96 + studentName + ", location=" + location + ", grade=" + grade 97 + "]"; 98 } 99 100 }
JDBCTools.java
1 package cky.test; 2 3 import java.io.IOException; 4 import java.io.InputStream; 5 import java.sql.Connection; 6 import java.sql.DriverManager; 7 import java.sql.PreparedStatement; 8 import java.sql.ResultSet; 9 import java.sql.SQLException; 10 import java.sql.Statement; 11 import java.util.Properties; 12 13 public class JDBCTools { 14 15 /** 16 * 执行 SQL 语句, 使用 PreparedStatement 17 * @param sql 18 * @param args: 填写 SQL 占位符的可变参数 19 */ 20 public static void update(String sql, Object ... args){ 21 Connection connection = null; 22 PreparedStatement preparedStatement = null; 23 24 try { 25 connection = JDBCTools.getConnection(); 26 preparedStatement = connection.prepareStatement(sql); 27 28 for(int i = 0; i < args.length; i++){ 29 preparedStatement.setObject(i + 1, args[i]); 30 } 31 32 preparedStatement.executeUpdate(); 33 34 } catch (Exception e) { 35 e.printStackTrace(); 36 } finally{ 37 JDBCTools.releaseDB(null, preparedStatement, connection); 38 } 39 } 40 41 /** 42 * 执行 SQL 的方法 43 * 44 * @param sql: insert, update 或 delete。 而不包含 select 45 */ 46 public static void update(String sql) { 47 Connection connection = null; 48 Statement statement = null; 49 50 try { 51 // 1. 获取数据库连接 52 connection = getConnection(); 53 54 // 2. 调用 Connection 对象的 createStatement() 方法获取 Statement 对象 55 statement = connection.createStatement(); 56 57 // 4. 发送 SQL 语句: 调用 Statement 对象的 executeUpdate(sql) 方法 58 statement.executeUpdate(sql); 59 60 } catch (Exception e) { 61 e.printStackTrace(); 62 } finally { 63 // 5. 关闭数据库资源: 由里向外关闭. 64 releaseDB(null, statement, connection); 65 } 66 } 67 68 /** 69 * 释放数据库资源的方法 70 * 71 * @param resultSet 72 * @param statement 73 * @param connection 74 */ 75 public static void releaseDB(ResultSet resultSet, Statement statement, 76 Connection connection) { 77 78 if (resultSet != null) { 79 try { 80 resultSet.close(); 81 } catch (SQLException e) { 82 e.printStackTrace(); 83 } 84 } 85 86 if (statement != null) { 87 try { 88 statement.close(); 89 } catch (SQLException e) { 90 e.printStackTrace(); 91 } 92 } 93 94 if (connection != null) { 95 try { 96 connection.close(); 97 } catch (SQLException e) { 98 e.printStackTrace(); 99 } 100 } 101 102 } 103 104 /** 105 * 获取数据库连接的方法 106 */ 107 public static Connection getConnection() throws IOException, 108 ClassNotFoundException, SQLException { 109 // 0. 读取 jdbc.properties 110 /** 111 * 1). 属性文件对应 Java 中的 Properties 类 2). 可以使用类加载器加载 bin 目录(类路径下)的文件 112 */ 113 Properties properties = new Properties(); 114 InputStream inStream = JDBCTools.class.getClassLoader() 115 .getResourceAsStream("jdbc.properties"); 116 properties.load(inStream); 117 118 // 1. 准备获取连接的 4 个字符串: user, password, jdbcUrl, driverClass 119 String user = properties.getProperty("user"); 120 String password = properties.getProperty("password"); 121 String jdbcUrl = properties.getProperty("url"); 122 String driverClass = properties.getProperty("driverClass"); 123 124 // 2. 加载驱动: Class.forName(driverClass) 125 Class.forName(driverClass); 126 127 // 3. 调用 128 // DriverManager.getConnection(jdbcUrl, user, password) 129 // 获取数据库连接 130 Connection connection = DriverManager.getConnection(jdbcUrl, user, 131 password); 132 return connection; 133 } 134 135 }
JDBCTest.java
1 package cky.test; 2 3 import java.sql.Connection; 4 5 import java.sql.PreparedStatement; 6 import java.sql.ResultSet; 7 8 import java.util.Scanner; 9 10 import org.junit.Test; 11 12 public class JDBCTest { 13 14 //得到学生的信息集 15 public Student getStudent(String sql, Object... args) { 16 Student stu = null; 17 18 Connection connection = null; 19 PreparedStatement preparedStatement = null; 20 ResultSet resultSet = null; 21 22 try { 23 connection = JDBCTools.getConnection(); 24 preparedStatement = connection.prepareStatement(sql); 25 for (int i = 0; i < args.length; i++) { 26 preparedStatement.setObject(i + 1, args[i]); 27 } 28 resultSet = preparedStatement.executeQuery(); 29 30 if (resultSet.next()) { 31 stu = new Student(); 32 stu.setFlowId(resultSet.getInt(1)); 33 stu.setType(resultSet.getInt(2)); 34 stu.setIdCard(resultSet.getString(3)); 35 36 } 37 38 } catch (Exception e) { 39 e.printStackTrace(); 40 } finally { 41 JDBCTools.releaseDB(resultSet, preparedStatement, connection); 42 } 43 44 return stu; 45 } 46 47 48 /* 49 private Student getStudent(String sql) { 50 51 Student stu = null; 52 53 Connection connection = null; 54 Statement statement = null; 55 ResultSet resultSet = null; 56 57 try { 58 connection = JDBCTools.getConnection(); 59 statement = connection.createStatement(); 60 resultSet = statement.executeQuery(sql); 61 62 if (resultSet.next()) { 63 stu = new Student(resultSet.getInt(1), resultSet.getInt(2), 64 resultSet.getString(3), resultSet.getString(4), 65 resultSet.getString(5), resultSet.getString(6), 66 resultSet.getInt(7)); 67 } 68 69 } catch (Exception e) { 70 e.printStackTrace(); 71 } finally { 72 JDBCTools.releaseDB(resultSet, statement, connection); 73 } 74 75 return stu; 76 } 77 */ 78 79 //打印学生信息: 若学生存在则打印其具体信息. 若不存在: 打印查无此人 80 private void printStudent(Student student) { 81 if (student != null) { 82 System.out.println(student); 83 } else { 84 System.out.println("查无此人!"); 85 } 86 } 87 88 // 从控制台读入一个整数, 确定要查询的类型; @return: 1. 用身份证查询. 2. 用准考证号查询 其他的无效. 并提示请用户重新输入. 89 private int getSearchTypeFromConsole() { 90 91 System.out.print("请输入查询类型: 1. 用身份证查询. 2. 用准考证号查询 "); 92 93 Scanner scanner = new Scanner(System.in); 94 int type = scanner.nextInt(); 95 96 if (type != 1 && type != 2) { 97 System.out.println("输入有误请重新输入!"); 98 throw new RuntimeException(); 99 } 100 101 return type; 102 } 103 104 //从控制台输入学生的信息 105 private Student getStudentFromConsole() { 106 107 Scanner scanner = new Scanner(System.in); 108 109 Student student = new Student(); 110 111 System.out.print("FlowId:"); 112 student.setFlowId(scanner.nextInt()); 113 114 System.out.print("Type: "); 115 student.setType(scanner.nextInt()); 116 117 System.out.print("IdCard:"); 118 student.setIdCard(scanner.next()); 119 120 System.out.print("ExamCard:"); 121 student.setExamCard(scanner.next()); 122 123 System.out.print("StudentName:"); 124 student.setStudentName(scanner.next()); 125 126 System.out.print("Location:"); 127 student.setLocation(scanner.next()); 128 129 System.out.print("Grade:"); 130 student.setGrade(scanner.nextInt()); 131 132 return student; 133 } 134 135 public void addNewStudent2(Student student) { 136 String sql = "INSERT INTO examstudent(flowid, type, idcard, " 137 + "examcard, studentname, location, grade) " 138 + "VALUES(?,?,?,?,?,?,?)"; 139 140 JDBCTools.update(sql, student.getFlowId(), student.getType(), 141 student.getIdCard(), student.getExamCard(), 142 student.getStudentName(), student.getLocation(), 143 student.getGrade()); 144 } 145 146 /* 147 public void addNewStudent(Student student) { 148 // 1. 准备一条 SQL 语句: 149 String sql = "INSERT INTO examstudent VALUES(" + student.getFlowId() 150 + "," + student.getType() + ",‘" + student.getIdCard() + "‘,‘" 151 + student.getExamCard() + "‘,‘" + student.getStudentName() 152 + "‘,‘" + student.getLocation() + "‘," + student.getGrade() 153 + ")"; 154 155 System.out.println(sql); 156 157 // 2. 调用 JDBCTools 类的 update(sql) 方法执行插入操作. 158 JDBCTools.update(sql); 159 } 160 */ 161 162 //具体查询学生信息的. 返回一个 Student 对象. 若不存在, 则返回 null 163 private Student searchStudent(int searchType) { 164 165 String sql = "SELECT flowid, type, idcard, examcard," 166 + "studentname, location, grade " + "FROM examstudent " 167 + "WHERE "; 168 169 Scanner scanner = new Scanner(System.in); 170 171 // 1. 根据输入的 searchType, 提示用户输入信息: 172 // 1.1 若 searchType 为 1, 提示: 请输入身份证号. 若为 2 提示: 请输入准考证号 173 // 2. 根据 searchType 确定 SQL 174 if (searchType == 1) { 175 System.out.print("请输入准考证号:"); 176 String examCard = scanner.next(); 177 sql = sql + "examcard = ‘" + examCard + "‘"; 178 } else { 179 System.out.print("请输入身份证号:"); 180 String examCard = scanner.next(); 181 sql = sql + "idcard = ‘" + examCard + "‘"; 182 } 183 184 // 3. 执行查询 185 Student student = getStudent(sql); 186 187 // 4. 若存在查询结果, 把查询结果封装为一个 Student 对象 188 189 return student; 190 } 191 192 193 //测试打印查询到的学生信息 194 @Test 195 public void testGetStudent() { 196 // 1. 得到查询的类型 197 int searchType = getSearchTypeFromConsole(); 198 199 // 2. 具体查询学生信息 200 Student student = searchStudent(searchType); 201 202 // 3. 打印学生信息 203 printStudent(student); 204 } 205 206 207 @Test 208 public void testAddNewStudent() { 209 Student student = getStudentFromConsole(); 210 addNewStudent2(student); 211 } 212 213 }
三、Statement 与 ResultSet
1.通过调用 Connection 对象的 createStatement 方法创建该对象
? Statement st = conn.createStatement();
2.该对象用于执行静态的 SQL 语句,并且返回执行结果
3.Statement 接口中定义了下列方法用于执行 SQL 语句:
? ResultSet excuteQuery(String sql)
? int excuteUpdate(String sql)
通用的INSERT、UPDATA、DELETE方法
1 //通用的 INSSERT UPDATE DELETE 方法(version 1.0) 2 public void update(String sql){ 3 //1.获取数据库的连接 4 Connection conn = null; 5 Statement st = null; 6 try{ 7 conn = JDBCUtils.getConnection(); 8 //2.提供一个 Statement 对象,将 sql 传递给数据库中执行 9 st = conn.createStatement(); 10 st.execute(sql); 11 }catch(Exception e){ 12 e.printStackTrace(); 13 }finally{ 14 //3.关闭 Statement 对象及连接 15 JDBCUtils.close(null, st, conn); 16 } 17 }
通用的查询方法,返回一个对象
1 public <T> T get(String sql, Class<T> clazz) { 2 Connection conn = null; 3 Statement st = null; 4 ResultSet rs = null; 5 T t = null; 6 try { 7 t = clazz.newInstance(); 8 conn = JDBCUtils.getConnection(); 9 st = conn.createStatement(); 10 rs = st.executeQuery(sql); 11 /* 12 * 通过 ResultSet 调用 getMetaData()返回一个结果集的元数据:ResultSetMetaData 13 * 14 * 1.getColumnCount():返回结果集的列数 15 * 2.getColumnLabel():返回列的别名 16 */ 17 ResultSetMetaData rsmd = rs.getMetaData(); 18 int columnCount = rsmd.getColumnCount(); 19 if (rs.next()) { 20 for (int i = 0; i < columnCount; i++) { 21 Object columnVal = rs.getObject(i + 1);// 相应列的值 22 //String columnName = rsmd.getColumnName(i + 1); 23 String columnName = rsmd.getColumnLabel(i + 1); 24 //使用 PropertyUtils 将指定对象 t 的指定属性 columnName 设置为指定的值 columnVal 25 PropertyUtils.setProperty(t, columnName, columnVal); 26 } 27 } 28 } catch (Exception e) { 29 e.printStackTrace(); 30 } finally { 31 JDBCUtils.close(rs, st, conn); 32 } 33 return t; 34 }
//通用的返回多个对象的查询操作
1 public <T> List<T> getInstances(String sql,Class<T> clazz){ 2 Connection conn = null; 3 Statement st = null; 4 ResultSet rs = null; 5 List<T> list = new ArrayList<T>(); 6 try { 7 conn = JDBCUtils.getConnection(); 8 st = conn.createStatement(); 9 rs = st.executeQuery(sql); 10 /* 11 * 通过 ResultSet 调用 getMetaData()返回一个结果集的元数据:ResultSetMetaData 12 * 13 * 1.getColumnCount():返回结果集的列数 14 * 2.getColumnLabel():返回列的别名 15 */ 16 ResultSetMetaData rsmd = rs.getMetaData(); 17 int columnCount = rsmd.getColumnCount(); 18 while (rs.next()) { 19 T t = clazz.newInstance(); 20 for (int i = 0; i < columnCount; i++) { 21 Object columnVal = rs.getObject(i + 1);// 相应列的值 22 //String columnName = rsmd.getColumnName(i + 1); 23 String columnName = rsmd.getColumnLabel(i + 1); 24 //使用 PropertyUtils 将指定对象 t 的指定属性 columnName 设置为指定的值 columnVal 25 PropertyUtils.setProperty(t, columnName, columnVal); 26 } 27 list.add(t); 28 } 29 } catch (Exception e) { 30 e.printStackTrace(); 31 } finally { 32 JDBCUtils.close(rs, st, conn); 33 } 34 return list; 35 }
或者采用这个方法(个人比较喜欢)
1 public List<Map<String, Object>> read(String sql) throws SQLException { 2 Connection conn = null; 3 PreparedStatement ps = null; 4 ResultSet rs = null; 5 try { 6 conn = JdbcUtils.getConnection(); 7 ps = conn.prepareStatement(sql); 8 rs = ps.executeQuery(); 9 ResultSetMetaData rsmd = rs.getMetaData(); 10 int count = rsmd.getColumnCount(); 11 String[] colNames = new String[count]; 12 System.out.println(count); 13 for (int i = 1; i <= count; i++) { 14 //Object val = rs.getObject(i); 15 //System.out.println(val); 16 //System.out.print(rsmd.getColumnClassName(i) + "\t"); 17 //System.out.print(rsmd.getColumnName(i) + "\t"); 18 //System.out.println(rsmd.getColumnLabel(i)); 19 colNames[i - 1] = rsmd.getColumnLabel(i); 20 } 21 List<Map<String, Object>> datas = new ArrayList<Map<String, Object>>(); 22 23 while (rs.next()) { 24 Map<String, Object> data = new HashMap<String, Object>(); 25 for (int i = 0; i < colNames.length; i++) { 26 data.put(colNames[i], rs.getObject(colNames[i])); 27 } 28 datas.add(data); 29 } 30 return datas; 31 } finally { 32 JdbcUtils.free(rs, ps, conn); 33 } 34 }
两种思想:
1.面向接口编程的思想;
2.ORM 思想:ORM:Object Relational Mapping
数据库中的表与 java 中的一个类对应(如: customers 表与 Customer 类对应);数据库中表的一个列与 java 类的一个属性对应(如:表中的 id 列与 Customer类的 id 属性对应);数据库中表的一行(一条数据)与 java 类的一个对象对应
1 package cn.itcast.jdbc; 2 3 import java.lang.reflect.InvocationTargetException; 4 import java.lang.reflect.Method; 5 import java.sql.Connection; 6 import java.sql.PreparedStatement; 7 import java.sql.ResultSet; 8 import java.sql.ResultSetMetaData; 9 import java.sql.SQLException; 10 import java.util.ArrayList; 11 import java.util.HashMap; 12 import java.util.List; 13 import java.util.Map; 14 15 import cn.itcast.jdbc.domain.User; 16 17 /** 18 * 19 * 2008-12-7 20 * 21 * @author <a href="mailto:[email protected]">liyong</a> 22 * 23 */ 24 public class ORMTest { 25 26 /** 27 * @param args 28 * @throws Exception 29 * @throws InvocationTargetException 30 * @throws IllegalAccessException 31 * @throws SQLException 32 */ 33 public static void main(String[] args) throws SQLException, 34 IllegalAccessException, InvocationTargetException, Exception { 35 User user = (User) getObject( 36 "select id as Id, name as Name, birthday as Birthday, money as Money from user where id=1", 37 User.class); 38 System.out.println(user); 39 40 Bean b = (Bean) getObject( 41 "select id as Id, name as Name, birthday as Birthday, money as Money from user where id=1", 42 Bean.class); 43 System.out.println(b); 44 } 45 46 static List<Object> getObjects(String sql, Class clazz) 47 throws SQLException, Exception, IllegalAccessException, 48 InvocationTargetException { 49 Connection conn = null; 50 PreparedStatement ps = null; 51 ResultSet rs = null; 52 try { 53 conn = JdbcUtils.getConnection(); 54 ps = conn.prepareStatement(sql); 55 rs = ps.executeQuery(); 56 String[] colNames = getColNames(rs); 57 58 List<Object> objects = new ArrayList<Object>(); 59 Method[] ms = clazz.getMethods(); 60 while (rs.next()) { 61 Object object = clazz.newInstance(); 62 for (int i = 0; i < colNames.length; i++) { 63 String colName = colNames[i]; 64 String methodName = "set" + colName; 65 // Object value = rs.getObject(colName); 66 // try { 67 // Method m = clazz 68 // .getMethod(methodName, value.getClass()); 69 // if (m != null) 70 // m.invoke(object, value); 71 // } catch (NoSuchMethodException e) { 72 // e.printStackTrace(); 73 // // 74 // } 75 for (Method m : ms) { 76 if (methodName.equals(m.getName())) { 77 m.invoke(object, rs.getObject(colName)); 78 break; 79 } 80 } 81 objects.add(object); 82 } 83 } 84 return objects; 85 } finally { 86 JdbcUtils.free(rs, ps, conn); 87 } 88 } 89 90 private static String[] getColNames(ResultSet rs) throws SQLException { 91 ResultSetMetaData rsmd = rs.getMetaData(); 92 int count = rsmd.getColumnCount(); 93 String[] colNames = new String[count]; 94 for (int i = 1; i <= count; i++) { 95 colNames[i - 1] = rsmd.getColumnLabel(i); 96 } 97 return colNames; 98 } 99 100 static Object getObject(String sql, Class clazz) throws SQLException, 101 Exception, IllegalAccessException, InvocationTargetException { 102 Connection conn = null; 103 PreparedStatement ps = null; 104 ResultSet rs = null; 105 try { 106 conn = JdbcUtils.getConnection(); 107 ps = conn.prepareStatement(sql); 108 rs = ps.executeQuery(); 109 String[] colNames = getColNames(rs); 110 111 Object object = null; 112 Method[] ms = clazz.getMethods(); 113 if (rs.next()) { 114 object = clazz.newInstance(); 115 for (int i = 0; i < colNames.length; i++) { 116 String colName = colNames[i]; 117 String methodName = "set" + colName; 118 // Object value = rs.getObject(colName); 119 // try { 120 // Method m = clazz 121 // .getMethod(methodName, value.getClass()); 122 // if (m != null) 123 // m.invoke(object, value); 124 // } catch (NoSuchMethodException e) { 125 // e.printStackTrace(); 126 // // 127 // } 128 for (Method m : ms) { 129 if (methodName.equals(m.getName())) { 130 m.invoke(object, rs.getObject(colName)); 131 break; 132 } 133 } 134 } 135 } 136 return object; 137 } finally { 138 JdbcUtils.free(rs, ps, conn); 139 } 140 } 141 }
两个技术:
1.结果集的元数据: ResultSetMetaData;
可用于获取关于 ResultSet 对象中列的类型和属性信息的对象:
—getColumnName(int column):获取指定列的名称
—getColumnCount():返回当前 ResultSet 对象中的列数。
—getColumnTypeName(int column):检索指定列的数据库特定的类型名称。
—getColumnDisplaySize(int column):指示指定列的最大标准宽度,以字符为单位。
—isNullable(int column):指示指定列中的值是否可以为 null。
—isAutoIncrement(int column):指示是否自动为指定列进行编号,这样这些列仍然是只读的。
1 public void testResultSetMetaData(){ 2 Connection conn = null; 3 Statement st = null; 4 ResultSet rs = null; 5 String sql = "select order_id id,order_name name,order_date date from `order`"; 6 try{ 7 conn = JDBCUtils.getConnection(); 8 st = conn.createStatement(); 9 rs = st.executeQuery(sql); 10 ResultSetMetaData rsmd = rs.getMetaData(); 11 int columnCount = rsmd.getColumnCount(); 12 System.out.println(columnCount); 13 while(rs.next()){ 14 for(int i = 0;i < columnCount;i++){ 15 System.out.print(rsmd.getColumnName(i + 1) + " "); 16 System.out.print(rsmd.getColumnLabel(i + 1) + " "); 17 System.out.println(rs.getObject(i + 1)); 18 } 19 System.out.println(); 20 } 21 }catch(Exception e){ 22 e.printStackTrace(); 23 }finally{ 24 JDBCUtils.close(rs, st, conn); 25 } 26 }
2.PropertyUtils:使用它的 setProperty(Object obj,String FieldName,Object FieldValue)
1 public void testPropertyUtils() throws Exception{ 2 Order order = new Order(); 3 System.out.println(order); 4 PropertyUtils.setProperty(order, "id", 1001); 5 PropertyUtils.setProperty(order, "name", "AA"); 6 PropertyUtils.setProperty(order, "date", new Date(new java.util.Date().getTime())); 7 System.out.println(order); 8 }
四、PreparedStatement
PreparedStatement 是 Statement 的子接口
a.需要预编译 SQL 语句: PreparedStatement ps = conn.preparedStatement(sql);
b.填充占位符: setObject(int index);//index 从 1 开始
c.execute() / executeUpdate() ; executeQuery(); 返回一个 ResultSet
1.替换原来的 Statement,实现增删改和查的操作
-->Statement 的问题:①拼串 不方便,容易出错 ②存在 sql 注入的问题,可以对数据库进行恶意攻击。
// 实现一个通用的 UPDATE INSERT DELETE 的操作的方法
1 public void update(String sql, Object... args) { 2 Connection conn = null; 3 PreparedStatement ps = null; 4 try { 5 // 1.获取连接 6 conn = JDBCUtils.getConnection(); 7 // 2.返回 PreparedSt 对象,预编译 sql 语句 8 ps = conn.prepareStatement(sql); 9 // 3.填充占位符 10 for (int i = 0; i < args.length; i++) { 11 ps.setObject(i + 1, args[i]); 12 } 13 ps.execute(); 14 } catch (Exception e) { 15 e.printStackTrace(); 16 } finally { 17 JDBCUtils.close(null, ps, conn); 18 } 19 }
// 实现一个通用的查询操作,返回一个对象
1 public <T> T getInstance(String sql, Class<T> clazz, Object... args) { 2 Connection conn = null; 3 PreparedStatement ps = null; 4 ResultSet rs = null; 5 try { 6 // 1.获取连接 7 conn = JDBCUtils.getConnection(); 8 // 2.预编译 sql 语句,返回 PreparedStatement 对象 9 ps = conn.prepareStatement(sql); 10 // 3.填充占位符 11 for (int i = 0; i < args.length; i++) { 12 ps.setObject(i + 1, args[i]); 13 } 14 // 4.执行并返回 ResultSet 的对象 15 rs = ps.executeQuery(); 16 if (rs.next()) { 17 // 5.创建 T 的对象 18 T t = clazz.newInstance(); 19 // 6.将结果集中的列值作为 T 的对象的属性,给予赋值 20 ResultSetMetaData rsmd = rs.getMetaData(); 21 int columnCount = rsmd.getColumnCount(); 22 for (int i = 0; i < columnCount; i++) { 23 Object columnVal = rs.getObject(i + 1); 24 String columnLabel = rsmd.getColumnLabel(i + 1); 25 PropertyUtils.setProperty(t, columnLabel, columnVal); 26 } 27 return t; 28 } 29 } catch (Exception e) { 30 e.printStackTrace(); 31 } finally { 32 // 7.关闭相应的操作 33 JDBCUtils.close(rs, ps, conn); 34 } 35 return null; 36 }
// 实现一个通用的查询操作,返回一个对象的集合
1 public <T> List<T> getForList(String sql,Class<T> clazz,Object ... args){ 2 Connection conn = null; 3 PreparedStatement ps = null; 4 ResultSet rs = null; 5 List<T> list = new ArrayList<T>(); 6 try{ 7 conn = JDBCUtils.getConnection(); 8 ps = conn.prepareStatement(sql); 9 for(int i = 0;i < args.length;i++){ 10 ps.setObject(i + 1, args[i]); 11 } 12 rs = ps.executeQuery(); 13 ResultSetMetaData rsmd = rs.getMetaData(); 14 int columnCount = rsmd.getColumnCount(); 15 while(rs.next()){ 16 T t = clazz.newInstance(); 17 for(int i = 0;i < columnCount;i++){ 18 Object columnVal = rs.getObject(i + 1); 19 String columnLabel = rsmd.getColumnLabel(i + 1); 20 PropertyUtils.setProperty(t, columnLabel, columnVal); 21 } 22 list.add(t); 23 } 24 }catch(Exception e){ 25 e.printStackTrace(); 26 }finally{ 27 JDBCUtils.close(rs, ps, conn); 28 } 29 return list; 30 }
2.使用 PreparedStatement 的其他优点
-->1.实现大数据类型的数据的插入、修改、查询的操作.
setBlob() getBlob();
// 从数据表中将大数据类型的数据取出
1 @Test 2 public void testBlob3(){ 3 Connection conn = null; 4 PreparedStatement ps = null; 5 String sql = "select id,name,email,birth,photo from customers where id = ?"; 6 ResultSet rs = null; 7 InputStream is = null; 8 FileOutputStream fos = null; 9 try{ 10 conn = JDBCUtils.getConnection(); 11 ps = conn.prepareStatement(sql); 12 fos = new FileOutputStream("ym1.jpg"); 13 ps.setInt(1, 21); 14 rs = ps.executeQuery(); 15 if(rs.next()){ 16 int id = rs.getInt("id"); 17 String name = rs.getString("name"); 18 Date birth = rs.getDate("birth"); 19 String email = rs.getString("email"); 20 Customer cust = new Customer(id,name,email,birth); 21 System.out.println(cust); 22 } 23 Blob photo = rs.getBlob(5);//获取此 ResultSet 对象的当前行中指定列的值 24 is = photo.getBinaryStream();//获取用于写入此 Blob 对象表示的 BLOB 值的流,输入流 25 26 byte[] b = new byte[1024]; 27 int len; 28 while((len = is.read(b)) != -1){ 29 fos.write(b, 0, len);//文件输出流,保存大数据文件到ym1.jpg中。 30 } 31 }catch (Exception e) { 32 e.printStackTrace(); 33 } finally { 34 JDBCUtils.close(rs, ps, conn); 35 if(fos != null){ 36 try { 37 fos.close(); 38 } catch (IOException e) { 39 // TODO Auto-generated catch block 40 e.printStackTrace(); 41 } 42 } 43 if(is != null){ 44 try { 45 is.close(); 46 } catch (IOException e) { 47 // TODO Auto-generated catch block 48 e.printStackTrace(); 49 } 50 } 51 } 52 }
// 向数据表中修改现有的大数据类型的数据
1 @Test 2 public void testBlob2() { 3 Connection conn = null; 4 PreparedStatement ps = null; 5 String sql = "update customers set photo = ? where id = ?"; 6 try { 7 conn = JDBCUtils.getConnection(); 8 ps = conn.prepareStatement(sql); 9 ps.setBlob(1, new FileInputStream("ym.jpg")); 10 ps.setInt(2, 21); 11 ps.execute(); 12 } catch (Exception e) { 13 e.printStackTrace(); 14 } finally { 15 JDBCUtils.close(null, ps, conn); 16 } 17 }
// 向数据库的表中写入大数据类型的数据
1 @Test 2 public void testBlob1() { 3 Connection conn = null; 4 PreparedStatement ps = null; 5 String sql = "insert into customers(name,email,birth,photo)values(?,?,?,?)"; 6 try { 7 conn = JDBCUtils.getConnection(); 8 ps = conn.prepareStatement(sql); 9 ps.setString(1, "杨幂 1"); 10 ps.setString(2, "[email protected]"); 11 ps.setDate(3, new Date(new java.util.Date().getTime())); 12 ps.setBlob(4, new FileInputStream("1.jpg")); 13 ps.execute(); 14 } catch (Exception e) { 15 e.printStackTrace(); 16 } finally { 17 JDBCUtils.close(null, ps, conn); 18 } 19 }
-->2.使用 PreparedStatement 进行批量操作时,效率优于 Statement.
oracle 是支持批量插入的。
如何实现最优? ①使用 PreparedStatement ②addBatch() executeBatch() clearBatch()
//批量操作,主要指的是批量插入
1 public void test4() { 2 Connection conn = null; 3 PreparedStatement ps = null; 4 long start = System.currentTimeMillis(); 5 String sql = "insert into dept values(?,?)"; 6 try { 7 conn = JDBCUtils.getConnection(); 8 ps = conn.prepareStatement(sql); 9 for (int i = 0; i < 100000; i++) { 10 ps.setInt(1, i + 1); 11 ps.setString(2, "dept_" + (i + 1) + "_name"); 12 //1.“攒” SQL 13 ps.addBatch(); 14 if( (i + 1) % 250 == 0){ 15 //2.执行 sql 16 ps.executeBatch(); 17 //3.清空 sql 18 ps.clearBatch(); 19 } 20 } 21 } catch (Exception e) { 22 e.printStackTrace(); 23 } finally { 24 JDBCUtils.close(null, ps, conn); 25 } 26 long end = System.currentTimeMillis(); 27 System.out.println("花费时间: " + (end - start));//2427 28 }
五、数据库的元数据: DataBaseMetaData(了解)
“元”数据: String name = "AA";
ResutSet :结果集
ResultSetMetaData:结果集的元数据
DatabaseMetaData:数据库的元数据
public class TestDataBaseMetaData { public static void main(String[] args) { Connection conn = null; DatabaseMetaData dbmd = null; ResultSet rs = null; try{ conn = JDBCUtils.getConnection(); //获取数据库的元数据 dbmd = conn.getMetaData(); //以字符串的形式返回数据库的名字 System.out.println(dbmd.getDatabaseProductName()); //返回数据库的版本号 System.out.println(dbmd.getDatabaseProductVersion()); rs = dbmd.getCatalogs(); //返回含有的各个数据库的名字 while(rs.next()){ String databaseName = rs.getString(1); System.out.println(databaseName); } } catch (Exception e) { e.printStackTrace(); } finally { JDBCUtils.close(rs, null, conn); } } }
六、数据库事务(重点)
1.事务:指构成单个逻辑工作单元的操作集合
2.事务处理:保证所有事务都作为一个工作单元来执行,即使出现了故障,都不能改变这种执行方式。当在一个事务中执行多个操作时,要么所有的事务都被提交(commit),要么整个事务回滚(rollback)到最初状态
2当一个连接对象被创建时,默认情况下是自动提交事务:每次执行一个 SQL 语句时,如果执行成功,就会向数据库自动提交,而不能回滚,为了让多个 SQL 语句作为一个事务执行:
a.调用 Connection 对象的 setAutoCommit(false); 以取消自动提交事务
b.在所有的 SQL 语句都成功执行后,调用 commit(); 方法提交事务
c.在出现异常时,调用 rollback(); 方法回滚事务
d.若此时 Connection 没有被关闭, 则需要恢复其自动提交状态
// 以下的两个操作共同构成一个数据库事务。但是在两个操作之间可能出现异常问题。
// 原则上,一旦出现问题,就需要将之前的操作“回滚”!需要对如下的操作进行完善。
1 @Test 2 public void testUpdate() { 3 String sql1 = "update user_table set balance = balance - 100 where user = ?"; 4 update(sql1, "AA"); 5 System.out.println(10 / 0); 6 String sql2 = "update user_table set balance = balance + 100 where user = ?"; 7 update(sql2, "BB"); 8 }
3.考虑到数据库事务的话,我们又将原来使用 PreparedStatement 重构的
Statement 的增删改和查的操作,再升级。
// 实现一个通用的 UPDATE INSERT DELETE 的操作的方法(version 3.0)
1 public void update(Connection conn,String sql, Object... args) { 2 PreparedStatement ps = null; 3 try { 4 ps = conn.prepareStatement(sql); 5 for (int i = 0; i < args.length; i++) { 6 ps.setObject(i + 1, args[i]); 7 } 8 ps.execute(); 9 } catch (Exception e) { 10 e.printStackTrace(); 11 } finally { 12 JDBCUtils.close(null, ps, null); 13 } 14 }
// 实现一个通用的查询操作,返回一个对象(version 3.0)
1 public <T> T getInstance(Connection conn,String sql, Class<T> clazz, Object... args) { 2 PreparedStatement ps = null; 3 ResultSet rs = null; 4 try { 5 ps = conn.prepareStatement(sql); 6 // 填充占位符 7 for (int i = 0; i < args.length; i++) { 8 ps.setObject(i + 1, args[i]); 9 } 10 // 4.执行并返回 ResultSet 的对象 11 rs = ps.executeQuery(); 12 if (rs.next()) { 13 // 5.创建 T 的对象 14 T t = clazz.newInstance(); 15 // 6.将结果集中的列值作为 T 的对象的属性,给予赋值 16 ResultSetMetaData rsmd = rs.getMetaData(); 17 int columnCount = rsmd.getColumnCount(); 18 for (int i = 0; i < columnCount; i++) { 19 Object columnVal = rs.getObject(i + 1); 20 String columnLabel = rsmd.getColumnLabel(i + 1); 21 PropertyUtils.setProperty(t, columnLabel, columnVal); 22 } 23 return t; 24 } 25 } catch (Exception e) { 26 e.printStackTrace(); 27 } finally { 28 // 7.关闭相应的操作 29 JDBCUtils.close(rs, ps, null); 30 } 31 return null; 32 }
// 实现一个通用的查询操作,返回一个对象的集合(version 3.0)
1 public <T> List<T> getForList(Connection conn,String sql,Class<T> clazz,Object ...args){ 2 PreparedStatement ps = null; 3 ResultSet rs = null; 4 List<T> list = new ArrayList<T>(); 5 try{ 6 ps = conn.prepareStatement(sql); 7 for(int i = 0;i < args.length;i++){ 8 ps.setObject(i + 1, args[i]); 9 } 10 rs = ps.executeQuery(); 11 ResultSetMetaData rsmd = rs.getMetaData(); 12 int columnCount = rsmd.getColumnCount(); 13 while(rs.next()){ 14 T t = clazz.newInstance(); 15 for(int i = 0;i < columnCount;i++){ 16 Object columnVal = rs.getObject(i + 1); 17 String columnLabel = rsmd.getColumnLabel(i + 1); 18 PropertyUtils.setProperty(t, columnLabel, columnVal); 19 } 20 list.add(t); 21 } 22 }catch(Exception e){ 23 e.printStackTrace(); 24 }finally{ 25 JDBCUtils.close(rs, ps, null); 26 } 27 return list; 28 }
//考虑到数据库事务,通过 java 程序对数据库中表的操作的模板(掌握)
1 public void method(){ 2 Connection conn = null; 3 try{ 4 //1.获取数据库的连接(①conn = JDBCUtils.getConnection(); ②数据库连接池(开发者选择此)) 5 //2.开启事务 6 conn.setAutoCommit(false); 7 //3.对数据库中表进行相应的操作(增、删、改、查)(①version 3.0 ②DBUtils 工具类: update() 和 query()方法) 8 //4.提交事务 9 conn.commit(); 10 }catch(Exception e){ 11 e.printStackTrace(); 12 try{ 13 //5.回滚事务 14 conn.rollback(); 15 }catch(Exception e1){ 16 e1.printStackTrace(); 17 } 18 }finally{ 19 //6.关闭数据库的连接(①自己实现数据库相应资源的关闭JDBCUtils.close(null,null,conn); ②使用 DBUtils 工具类的 close()方法) 20 } 21 }
事务的ACID(acid)属性:
1. 原子性(Atomicity)原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
2. 一致性(Consistency)事务必须使数据库从一个一致性状态变换到另外一个一致性状态。
3. 隔离性(Isolation)事务的隔离性是指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
4. 持久性(Durability)持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来的其他操作和数据库故障不应该对其有任何影响
数据库的隔离级别:
1.对于同时运行的多个事务, 当这些事务访问数据库中相同的数据时, 如果没有采取必要的隔离机制, 就会导致各种并发问题:
a.脏读: 对于两个事物 T1, T2, T1 读取了已经被 T2 更新但还没有被提交的字段. 之后, 若 T2 回滚, T1读取的内容就是临时且无效的.
b.不可重复读: 对于两个事物 T1, T2, T1 读取了一个字段, 然后 T2 更新了该字段. 之后, T1再次读取同一个字段, 值就不同了.
c.幻读: 对于两个事物 T1, T2, T1 从一个表中读取了一个字段, 然后 T2 在该表中插入了一些新的行. 之后, 如果 T1 再次读取同一个表, 就会多出几行.
2.数据库事务的隔离性: 数据库系统必须具有隔离并发运行各个事务的能力, 使它们不会相互影响, 避免各种并发问题.
3.一个事务与其他事务隔离的程度称为隔离级别. 数据库规定了多种事务隔离级别, 不同隔离级别对应不同的干扰程度, 隔离级别越高, 数据一致性就越好, 但并发性越弱
七、实现 DAO 及其实现类 CustomerDAO 的代码
DAO.java
1 //DAO: database access object 2 class ReflectionUtils{ 3 //获取 clazz 对象对应的运行时类的父类的泛型 4 public static Class getSuperGeneric(Class clazz){ 5 Type type = clazz.getGenericSuperclass(); 6 ParameterizedType p = (ParameterizedType)type; 7 Type[] ts = p.getActualTypeArguments(); 8 return (Class)ts[0]; 9 } 10 } 11 12 public class DAO<T> { 13 private Class<T> clazz = null; 14 //this.getClass()在这个问题中,就是 CustomerDAO 15 public DAO(){ 16 clazz = ReflectionUtils.getSuperGeneric(this.getClass()); 17 } 18 19 //获取数据库的标准的特定含义的值 20 public <E> E getValue(Connection conn,String sql,Object...args){ 21 PreparedStatement ps = null; 22 ResultSet rs = null; 23 try{ 24 ps = conn.prepareStatement(sql); 25 for(int i = 0;i < args.length;i++){ 26 ps.setObject(i + 1, args[i]); 27 } 28 rs = ps.executeQuery(); 29 if(rs.next()){ 30 return (E)rs.getObject(1); 31 } 32 }catch(Exception e){ 33 e.printStackTrace(); 34 }finally{ 35 JDBCUtils.close(rs, ps, null); 36 } 37 return null; 38 } 39 40 //返回多个对象,以集合的形式返回 41 public List<T> getForList(Connection conn,String sql,Object ...args){ 42 PreparedStatement ps = null; 43 ResultSet rs = null; 44 List<T> list = new ArrayList<>(); 45 try{ 46 //1.预编译 sql 语句,获取 PreparedStatement 对象 47 ps = conn.prepareStatement(sql); 48 //2.填充占位符 49 for(int i = 0;i < args.length;i++){ 50 ps.setObject(i + 1, args[i]); 51 } 52 //3.返回一个结果集 53 rs = ps.executeQuery(); 54 ResultSetMetaData rsmd = rs.getMetaData(); 55 int columnCount = rsmd.getColumnCount(); 56 while(rs.next()){ 57 T t = clazz.newInstance(); 58 //给 t 对象的相应属性赋值 59 for(int i = 0;i < columnCount;i++){ 60 Object columnVal = rs.getObject(i + 1); 61 String columnLabel = rsmd.getColumnLabel(i + 1); 62 PropertyUtils.setProperty(t, columnLabel, columnVal); 63 } 64 list.add(t); 65 } 66 }catch(Exception e){ 67 e.printStackTrace(); 68 }finally{ 69 JDBCUtils.close(rs, ps, null); 70 } 71 //System.out.println(clazz); 72 return list; 73 } 74 75 //返回一个对象 76 public T get(Connection conn,String sql,Object ...args){ 77 PreparedStatement ps = null; 78 ResultSet rs = null; 79 try{ 80 //1.预编译 sql 语句,获取 PreparedStatement 对象 81 ps = conn.prepareStatement(sql); 82 //2.填充占位符 83 for(int i = 0;i < args.length;i++){ 84 ps.setObject(i + 1, args[i]); 85 } 86 //3.返回一个结果集 87 rs = ps.executeQuery(); 88 ResultSetMetaData rsmd = rs.getMetaData(); 89 int columnCount = rsmd.getColumnCount(); 90 if(rs.next()){ 91 T t = clazz.newInstance(); 92 //给 t 对象的相应属性赋值 93 for(int i = 0;i < columnCount;i++){ 94 Object columnVal = rs.getObject(i + 1); 95 String columnLabel = rsmd.getColumnLabel(i + 1); 96 PropertyUtils.setProperty(t, columnLabel, columnVal); 97 } 98 return t; 99 } 100 }catch(Exception e){ 101 e.printStackTrace(); 102 }finally{ 103 JDBCUtils.close(rs, ps, null); 104 } 105 //System.out.println(clazz); 106 return null; 107 } 108 109 //通用的增删改的操作 110 public void update(Connection conn,String sql,Object ... args){ 111 PreparedStatement ps = null; 112 try{ 113 ps = conn.prepareStatement(sql); 114 for(int i = 0;i < args.length;i++){ 115 ps.setObject(i + 1, args[i]); 116 } 117 ps.executeUpdate(); 118 }catch(Exception e){ 119 e.printStackTrace(); 120 }finally{ 121 JDBCUtils.close(null, ps, null); 122 } 123 } 124 }
Customer.java
1 //CustomerDAO 类是用来操作 Customer 类的 2 public class CustomerDAO extends DAO<Customer>{ 3 4 // @Test 5 // public void testGeneric(){ 6 // Class clazz = CustomerDAO.class; 7 // Type type = clazz.getGenericSuperclass(); 8 // ParameterizedType p = (ParameterizedType)type; 9 // Type[] ts = p.getActualTypeArguments(); 10 // System.out.println(ts[0]); 11 // } 12 13 /** 14 * 获取对应的表中的记录的个数 15 */ 16 public long getCount(Connection conn){ 17 String sql = "select count(*) from customers"; 18 return (long)getValue(conn, sql); 19 } 20 21 /** 22 * 返回 customers 表中的所有数据 23 * @param conn 24 * @return 25 */ 26 public List<Customer> getAll(Connection conn){ 27 String sql = "select id,name,email,birth from customers"; 28 return getForList(conn, sql); 29 } 30 31 /** 32 * 根据指定的 id 返回相应的对象 33 * @param conn 34 * @param customerId 35 */ 36 public Customer getInstance(Connection conn,int customerId){ 37 String sql = "select id,name,email,birth from customers where id = ?"; 38 return get(conn, sql, customerId); 39 } 40 41 /** 42 * 删除指定 customerId 的数据表中的记录 43 * @param conn 44 * @param customerId 45 */ 46 public void delete(Connection conn,int customerId){ 47 String sql = "delete from customers where id = ?"; 48 update(conn, sql, customerId); 49 } 50 51 /** 52 * 向数据表中修改指定 id 的信息为 Customer 对象的信息 53 * @param conn 54 * @param cust 55 */ 56 public void update(Connection conn,Customer cust){ 57 String sql = "update customers set name = ?,email = ?,birth = ? where id = ?"; 58 update(conn, sql,cust.getName(),cust.getEmail(),cust.getBirth(),cust.getId()); 59 } 60 61 /** 62 * 向数据表中插入一条数据 63 * @param conn 数据库的连接 64 * @param cust 要插入的 Customer 对象 65 */ 66 public void insert(Connection conn,Customer cust){ 67 String sql = "insert into customers(name,email,birth)values(?,?,?)"; 68 update(conn, sql, cust.getName(),cust.getEmail(),cust.getBirth()); 69 } 70 }
TestCustomer.java
1 public class TestCustomerDAO { 2 CustomerDAO customerDAO = new CustomerDAO(); 3 4 @Test 5 public void testGetCount(){ 6 Connection conn = null; 7 try{ 8 conn = JDBCUtils.getConnection(); 9 long count = customerDAO.getCount(conn); 10 System.out.println(count); 11 }catch(Exception e){ 12 e.printStackTrace(); 13 }finally{ 14 JDBCUtils.close(null, null, conn); 15 } 16 } 17 18 @Test 19 public void testGetAll(){ 20 Connection conn = null; 21 try{ 22 conn = JDBCUtils.getConnection(); 23 List<Customer> list = customerDAO.getAll(conn); 24 //System.out.println(list); 25 Iterator<Customer> iterator = list.iterator(); 26 while(iterator.hasNext()){ 27 System.out.println(iterator.next()); 28 } 29 }catch(Exception e){ 30 e.printStackTrace(); 31 }finally{ 32 JDBCUtils.close(null, null, conn); 33 } 34 } 35 36 @Test 37 public void testQuery(){ 38 Connection conn = null; 39 try{ 40 conn = JDBCUtils.getConnection(); 41 Customer cust = customerDAO.getInstance(conn, 13); 42 System.out.println(cust); 43 }catch(Exception e){ 44 e.printStackTrace(); 45 }finally{ 46 JDBCUtils.close(null, null, conn); 47 } 48 } 49 50 @Test 51 public void testDelete(){ 52 Connection conn = null; 53 try{ 54 conn = JDBCUtils.getConnection(); 55 customerDAO.delete(conn, 10); 56 }catch(Exception e){ 57 e.printStackTrace(); 58 }finally{ 59 JDBCUtils.close(null, null, conn); 60 } 61 } 62 63 @Test 64 public void testUpdate(){ 65 Connection conn = null; 66 try{ 67 conn = JDBCUtils.getConnection(); 68 Customer cust = new Customer(10, "张卫健", "[email protected]",new 69 Date(new java.util.Date().getTime())); 70 customerDAO.update(conn, cust); 71 }catch(Exception e){ 72 e.printStackTrace(); 73 }finally{ 74 JDBCUtils.close(null, null, conn); 75 } 76 } 77 78 @Test 79 public void testInsert(){ 80 Connection conn = null; 81 try{ 82 conn = JDBCUtils.getConnection(); 83 Customer cust = new Customer(10, "张卫健", "[email protected]",new 84 Date(new java.util.Date().getTime())); 85 customerDAO.insert(conn, cust); 86 }catch(Exception e){ 87 e.printStackTrace(); 88 }finally{ 89 JDBCUtils.close(null, null, conn); 90 } 91 } 92 }
八、数据库连接池
C3P0 数据库连接池
1 // 保证在所有的通过 C3P0 获取的连接中,只有一个 DataSource 的对象。(推荐) 2 private static DataSource source = null; 3 4 static { 5 source = new ComboPooledDataSource("helloc3p0"); 6 } 7 // 获取数据库的连接方式 3:使用 c3p0 数据库连接池获取数据库的连接,使用配置文件 8 public static Connection getConnection3() throws Exception { 9 return source.getConnection(); 10 }
对应的配置文件: c3p0-config.xml
1 <c3p0-config> 2 <named-config name="helloc3p0"> 3 <!-- 提供数据库连接的 4 个基本信息 --> 4 <property name="jdbcUrl">jdbc:mysql:///test</property> 5 <property name="driverClass">com.mysql.jdbc.Driver</property> 6 <property name="user">root</property> 7 <property name="password">123456</property> 8 <!-- 当连接池中的数量不足时, c3p0 连接一次性向数据库服务器申请的 9 连接数 --> 10 <property name="acquireIncrement">5</property> 11 <!-- 初始化数据库连接池时,池中存在的连接数 --> 12 <property name="initialPoolSize">10</property> 13 <!-- 数据库连接池中最少容纳的连接数 --> 14 <property name="minPoolSize">5</property> 15 <!-- 数据库连接池中最大容纳的连接数 --> 16 <property name="maxPoolSize">100</property> 17 <!-- 连接池中,最多允许存在的 Statement 的数量 --> 18 <property name="maxStatements">10</property> 19 <!-- 一次连接中,最多容纳的 Statement 的个数 --> 20 <property name="maxStatementsPerConnection">5</property> 21 </named-config> 22 </c3p0-config>
DBCP 数据库连接池
1 //随着类的加载,使用 BasicDataSourceFactory 的静态方法 createDataSource()返回一个 2 //DataSource 的对象 3 private static DataSource source1 = null; 4 static { 5 Properties info = new Properties(); 6 // info.load(new FileInputStream("dbcp.properties")); 7 InputStream is = JDBCUtils.class.getClassLoader().getResourceAsStream( 8 "com/atguigu/java/dbcp.properties"); 9 try { 10 info.load(is); 11 source1 = BasicDataSourceFactory.createDataSource(info); 12 } catch (Exception e) { 13 e.printStackTrace(); 14 } 15 } 16 // 获取数据库的连接方式 4:使用 DBCP 数据库连接池获取数据库的连接(推荐) 17 public static Connection getConnection5() throws Exception { 18 return source1.getConnection(); 19 }
配置文件 dbcp.properties:
1 username=root 2 password=123456 3 url=jdbc:mysql://127.0.0.1:3306/test 4 driverClassName=com.mysql.jdbc.Driver 5 initialSize=10 6 maxActive=100
九、 DBUtils
提供了 QueryRunner 类,类中有诸多重载 update() 和 query()方法,供使用,用于堆数据库实现操作:增删改查
一些细节问题:
1.设计数据库时候,要考虑编码问题,要主要,创建数据库编码方式和创建表的方式以及java程序运行的编码方式一致,不然会报错,或者导致数据存入到数据库中出现乱码。
2.关于JDBC实现数据库连接时,对日期对象的处理方式:
1 public class DateTest { 2 3 /** 4 * @param args 5 * @throws SQLException 6 */ 7 public static void main(String[] args) throws SQLException { 8 // create("name2", new Date(), 500.0f); 9 Date d = read(7); 10 System.out.println(d); 11 } 12 13 static Date read(int id) throws SQLException { 14 Connection conn = null; 15 Statement st = null; 16 ResultSet rs = null; 17 Date birthday = null; 18 try { 19 // 2.建立连接 20 conn = JdbcUtils.getConnection(); 21 // conn = JdbcUtilsSing.getInstance().getConnection(); 22 // 3.创建语句 23 st = conn.createStatement(); 24 25 // 4.执行语句 26 rs = st.executeQuery("select birthday from user where id=" + id); 27 28 // 5.处理结果 29 while (rs.next()) { 30 //birthday = new Date(rs.getDate("birthday").getTime()); 31 birthday = rs.getDate("birthday"); 32 } 33 } finally { 34 JdbcUtils.free(rs, st, conn); 35 } 36 return birthday; 37 } 38 39 static void create(String name, Date birthday, float money) 40 throws SQLException { 41 Connection conn = null; 42 PreparedStatement ps = null; 43 ResultSet rs = null; 44 try { 45 // 2.建立连接 46 conn = JdbcUtils.getConnection(); 47 // conn = JdbcUtilsSing.getInstance().getConnection(); 48 // 3.创建语句 49 String sql = "insert into user(name,birthday, money) values (?, ?, ?) "; 50 ps = conn.prepareStatement(sql); 51 ps.setString(1, name); 52 ps.setDate(2, new java.sql.Date(birthday.getTime())); 53 ps.setFloat(3, money); 54 55 // 4.执行语句 56 int i = ps.executeUpdate(); 57 58 System.out.println("i=" + i); 59 } finally { 60 JdbcUtils.free(rs, ps, conn); 61 } 62 } 63 }