JDBC概述
JDBC(Java database Connectivity)是一个独立于特定数据管理系统、通用的 SQL 数据库存取和操作的公共接口
JDBC为访问不同的数据库提供了一种统一的途径 ,为开发者屏蔽了一些细节问题。
JDBC的目标是使Java程序员使用JDBC可以连接任何提供了JDBC驱动程序 的数据库系统,这样就使得程序员无需对特定的数据库系统的特点有过多的了解,从而大大简化和加快了开发过程。
驱动下载和引用 首先进入官方地址
https://dev.mysql.com/downloads/connector/j/
打开 idea 在项目目录新建一个文件夹,并起名为 lib
然后将上面复制的 jar 包粘贴到该文件夹下,点击OK
然后再 jar 包上右键选择 Add as Libary
点击OK完成驱动导入
连接数据库 方式一 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @Test public void test1 () throws SQLException { Driver driver = new Driver(); String url = "jdbc:mysql://localhost:3306/test" ; Properties info = new Properties(); info.setProperty("user" ,"root" ); info.setProperty("password" ,"abc123" ); Connection connect = driver.connect(url, info); System.out.println(connect); }
方式二 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Test public void test2 () throws Exception { Class<?> aClass = Class.forName("com.mysql.cj.jdbc.Driver" ); Driver driver = (Driver) aClass.newInstance(); String url = "jdbc:mysql://localhost:3306/test" ; Properties info = new Properties(); info.setProperty("user" ,"root" ); info.setProperty("password" ,"abc123" ); Connection connect = driver.connect(url, info); System.out.println(connect); }
方式三 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public void test3 () throws Exception { Class<?> aClass = Class.forName("com.mysql.cj.jdbc.Driver" ); Driver driver = (Driver) aClass.newInstance(); String url = "jdbc:mysql://127.0.0.1:3306/test" ; String user = "root" ; String password = "abc123" ; DriverManager.registerDriver(driver); Connection connection = DriverManager.getConnection(url, user, password); System.out.println(connection); }
方式四 这种方式省略了注册驱动的过程,这是因为 Driver 类中有一个静态代码块,里面帮我们写了注册的逻辑,在类加载时自动触发
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public void test4 () throws Exception { String url = "jdbc:mysql://127.0.0.1:3306/test" ; String user = "root" ; String password = "abc123" ; Class.forName("com.mysql.cj.jdbc.Driver" ); Connection connection = DriverManager.getConnection(url, user, password); System.out.println(connection); }
方式五:最终版 通过读取配置文件的方式优点
对代码内容进行解耦,通过修改配置文件就可以实现数据库连接的修改
首先新建配置文件
1 2 3 4 user =root password =abc123 url =jdbc:mysql://127.0.0.1:3306/jdbc_learn driverClass =com.mysql.cj.jdbc.Driver
然后编写代码测试连接
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @Test public void test5 () throws Exception { Properties properties = new Properties(); FileInputStream fis = new FileInputStream("lib/jdbc.properties" ); properties.load(fis); String url = properties.getProperty("url" ); String user = properties.getProperty("user" ); String password = properties.getProperty("password" ); Class.forName(properties.getProperty("driverClass" )); Connection connection = DriverManager.getConnection(url, user, password); System.out.println(connection); }
使用 prepareStatement 插入数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 package com.songzx.jdbc;import java.io.FileInputStream;import java.io.IOException;import java.sql.*;import java.text.ParseException;import java.text.SimpleDateFormat;import java.util.Properties;public class exer02 { public static void main (String[] args) { Connection conn = null ; PreparedStatement pst = null ; try { Properties properties = new Properties(); FileInputStream fis = new FileInputStream("main/lib/jdbc.properties" ); properties.load(fis); String url = properties.getProperty("url" ); String user = properties.getProperty("user" ); String password = properties.getProperty("password" ); Class.forName(properties.getProperty("driverClass" )); conn = DriverManager.getConnection(url, user, password); String sql = "INSERT INTO customers(`name`,email,birth) VALUES (?,?,?)" ; pst = conn.prepareStatement(sql); pst.setString(1 ,"哪吒" ); pst.setString(2 ,"nezha@163.com" ); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd" ); java.util.Date date = sdf.parse("2000-01-06" ); pst.setDate(3 ,new Date(date.getTime())); pst.execute(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException throwables) { throwables.printStackTrace(); } catch (ParseException e) { e.printStackTrace(); } finally { try { if (conn!=null ){ conn.close(); } } catch (SQLException throwables) { throwables.printStackTrace(); } try { if (pst!=null ){ pst.close(); } } catch (SQLException throwables) { throwables.printStackTrace(); } } } }
封装 SQL 连接方法和关闭方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 package com.songzx.util;import java.io.FileInputStream;import java.sql.Connection;import java.sql.DriverManager;import java.sql.SQLException;import java.sql.Statement;import java.util.Properties;public class JdbcUtils { public static Connection getConnection () throws Exception { Properties properties = new Properties(); FileInputStream fis = new FileInputStream("main/lib/jdbc.properties" ); properties.load(fis); String url = properties.getProperty("url" ); String user = properties.getProperty("user" ); String password = properties.getProperty("password" ); Class.forName(properties.getProperty("driverClass" )); Connection connection = DriverManager.getConnection(url, user, password); return connection; } public static void closeConnection (Connection conn, Statement pst) { try { if (conn!=null ){ conn.close(); } } catch (SQLException throwables) { throwables.printStackTrace(); } try { if (pst!=null ){ pst.close(); } } catch (SQLException throwables) { throwables.printStackTrace(); } } }
更新数据 使用封装后的方法,简化代码操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public static void main (String[] args) throws Exception { Connection conn = JdbcUtils.getConnection(); String sql = "UPDATE customers SET `name` = ? WHERE id = ?" ; PreparedStatement pst = conn.prepareStatement(sql); pst.setString(1 ,"莫扎特" ); pst.setInt(2 ,18 ); pst.execute(); JdbcUtils.closeConnection(conn,pst); }
封装一个通用的执行 SQL 方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public static void executeSql (String sql,Object ...arg) { Connection conn = null ; PreparedStatement pst = null ; try { conn = JdbcUtils.getConnection(); pst = conn.prepareStatement(sql); for (int i = 0 ; i < arg.length; i++) { pst.setObject(i+1 ,arg[i]); } pst.execute(); } catch (Exception e) { e.printStackTrace(); } finally { JdbcUtils.closeConnection(conn,pst); } }
使用上面封装的方法删除数据
1 2 3 4 5 public static void main (String[] args) { String sql = "DELETE FROM customers WHERE id = ?" ; JdbcUtils.executeSql(sql,3 ); }
使用上面的方法更新数据
1 2 3 4 public static void main (String[] args) { String sql2 = "UPDATE `order` SET order_name = ? WHERE order_id = ?" ; JdbcUtils.executeSql(sql2,"DD" ,2 ); }
执行查询方法 首先新建一个对应 customers 表的 Customers类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 package com.songzx.bean;import java.sql.Date;public class Customers { int id; String name; String email; Date birth; public Customers () { } public Customers (int id, String name, String email, Date birth) { this .id = id; this .name = name; this .email = email; this .birth = birth; } public int getId () { return id; } public void setId (int id) { this .id = id; } public String getName () { return name; } public void setName (String name) { this .name = name; } public String getEmail () { return email; } public void setEmail (String email) { this .email = email; } public Date getBirth () { return birth; } public void setBirth (Date birth) { this .birth = birth; } @Override public String toString () { return "Customers{" + "id=" + id + ", name='" + name + '\'' + ", email='" + email + '\'' + ", birth=" + birth + '}' ; } }
在 JdbcUtils 中添加重载的关闭方法,增加一个参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public static void closeConnection (Connection conn, Statement pst,ResultSet res) { try { if (conn!=null ){ conn.close(); } } catch (SQLException throwables) { throwables.printStackTrace(); } try { if (pst!=null ){ pst.close(); } } catch (SQLException throwables) { throwables.printStackTrace(); } try { if (res!=null ){ res.close(); } } catch (SQLException throwables) { throwables.printStackTrace(); } }
处理结果集获取返回值
调用 pst.executeQuery() 方法返回一个结果集
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public static void main (String[] args) throws Exception { Connection conn = JdbcUtils.getConnection(); String sql = "select id,name,email,birth from customers where id = ?" ; PreparedStatement pst = conn.prepareStatement(sql); pst.setInt(1 ,1 ); ResultSet resultSet = pst.executeQuery(); if (resultSet.next()){ int id = resultSet.getInt(1 ); String name = resultSet.getString(2 ); String email = resultSet.getString(3 ); Date birth = resultSet.getDate(4 ); Customers cus = new Customers(id, name, email, birth); System.out.println(cus); } JdbcUtils.closeConnection(conn,pst,resultSet); }
执行方法正确返回查询到的值
Java和SQL数据类型对应表
Java类型
SQL类型
boolean
BIT
byte
TINYINT
short
SMALLINT
int
INTEGER
long
BIGINT
String
CHAR,VARCHAR,LONGVARCHAR
byte array
BINARY , VAR BINARY
java.sql.Date
DATE
java.sql.Time
TIME
java.sql.Timestamp
TIMESTAMP
ORM 编程思想
一个数据表对应一个 Java 类
表中一条记录对应 Java 类的一个对象
表中一个字段对应 Java 类中的一个属性
实现一个 customers 表的通用查询方法 用到的新方法:
ResultSetMetaData rsmd = resultSet.getMetaData();
获取结果集的元数据
int columnCount = rsmd.getColumnCount();
获取结果集中的列数
String columnName = rsmd.getColumnName(int index);
查询当前列名
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 package com.songzx.jdbc;import com.songzx.bean.Customers;import com.songzx.util.JdbcUtils;import java.lang.reflect.Field;import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.sql.ResultSetMetaData;public class exer06 { public static void main (String[] args) { String sql = "select name,email,birth from customers where id = ?" ; Customers cus = new exer06().customersQuery(sql, 4 ); System.out.println(cus); } public Customers customersQuery (String sql,Object ...arg) { Connection conn = null ; PreparedStatement pst = null ; ResultSet resultSet = null ; try { conn = JdbcUtils.getConnection(); pst = conn.prepareStatement(sql); for (int i = 0 ; i < arg.length; i++) { pst.setObject(i+1 ,arg[i]); } resultSet = pst.executeQuery(); ResultSetMetaData rsmd = resultSet.getMetaData(); int columnCount = rsmd.getColumnCount(); if (resultSet.next()){ Customers cus = new Customers(); for (int i = 0 ; i < columnCount; i++) { String columnName = rsmd.getColumnName(i + 1 ); Object columnValue = resultSet.getObject(i + 1 ); Field field = Customers.class.getDeclaredField(columnName); field.setAccessible(true ); field.set(cus,columnValue); } return cus; } } catch (Exception e) { e.printStackTrace(); } finally { JdbcUtils.closeConnection(conn,pst,resultSet); } return null ; } }
运行测试,正常返回。因为没有给 id 赋值,所以id默认是0
实现一个 order 表的通用查询方法
metaData.getColumnLabel
方法获取列的别名,如果没有别名则获取本身的列名
如果数据库的列名和类中的属性不一致,可以在 SQL 语句中给列起别名
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 public class exer07 { public static void main (String[] args) { String sql = "select order_id orderId,order_name orderName,order_date orderDate from `order` where order_id = ?" ; Order order = new exer07().orderQuery(sql, 1 ); System.out.println(order); } public Order orderQuery (String sql,Object ...arg) { Connection conn = null ; PreparedStatement pst = null ; ResultSet resultSet = null ; try { conn = JdbcUtils.getConnection(); pst = conn.prepareStatement(sql); for (int i = 0 ; i < arg.length; i++) { pst.setObject(i+1 ,arg[i]); } resultSet = pst.executeQuery(); ResultSetMetaData metaData = resultSet.getMetaData(); int columnCount = metaData.getColumnCount(); if (resultSet.next()){ Order order = new Order(); for (int i = 0 ; i < columnCount; i++) { Object value = resultSet.getObject(i + 1 ); String label = metaData.getColumnLabel(i + 1 ); Field field = Order.class.getDeclaredField(label); field.setAccessible(true ); field.set(order,value); } return order; } } catch (Exception e) { e.printStackTrace(); } finally { JdbcUtils.closeConnection(conn,pst,resultSet); } return null ; } }
实现不同表的通用查询方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 public class exer08 { public static void main (String[] args) { String sql = "select id,name,email,birth from customers where id = ?" ; Customers customers = new exer08().classQuery(Customers.class, sql, 2 ); System.out.println(customers); String sql1 = "select order_id orderId,order_name orderName,order_date orderDate from `order` where order_id = ?" ; Order order = new exer08().classQuery(Order.class, sql1, 2 ); System.out.println(order); } public <T> T classQuery (Class<T> cazz, String sql, Object ...arg) { Connection conn = null ; PreparedStatement pst = null ; ResultSet resultSet = null ; try { conn = JdbcUtils.getConnection(); pst = conn.prepareStatement(sql); for (int i = 0 ; i < arg.length; i++) { pst.setObject(i+1 ,arg[i]); } resultSet = pst.executeQuery(); ResultSetMetaData metaData = resultSet.getMetaData(); int columnCount = metaData.getColumnCount(); if (resultSet.next()){ T t = cazz.newInstance(); for (int i = 0 ; i < columnCount; i++) { Object value = resultSet.getObject(i + 1 ); String label = metaData.getColumnLabel(i + 1 ); Field field = cazz.getDeclaredField(label); field.setAccessible(true ); field.set(t,value); } return t; } } catch (Exception e) { e.printStackTrace(); } finally { JdbcUtils.closeConnection(conn,pst,resultSet); } return null ; } }
实现不同表的通用查询方法,返回多条数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 public class exer09 { public static void main (String[] args) { String sql = "select id,name,email,birth from customers where id < ?" ; ArrayList<Customers> customers = new exer09().classQuery(Customers.class, sql,5 ); for (Customers customer : customers) { System.out.println(customer); } } public <T> ArrayList<T> classQuery (Class<T> cazz, String sql, Object ...arg) { Connection conn = null ; PreparedStatement pst = null ; ResultSet resultSet = null ; try { conn = JdbcUtils.getConnection(); pst = conn.prepareStatement(sql); for (int i = 0 ; i < arg.length; i++) { pst.setObject(i+1 ,arg[i]); } resultSet = pst.executeQuery(); ResultSetMetaData metaData = resultSet.getMetaData(); int columnCount = metaData.getColumnCount(); ArrayList<T> ts = new ArrayList<T>(); while (resultSet.next()){ T t = cazz.newInstance(); for (int i = 0 ; i < columnCount; i++) { Object value = resultSet.getObject(i + 1 ); String label = metaData.getColumnLabel(i + 1 ); Field field = cazz.getDeclaredField(label); field.setAccessible(true ); field.set(t,value); } ts.add(t); } return ts; } catch (Exception e) { e.printStackTrace(); } finally { JdbcUtils.closeConnection(conn,pst,resultSet); } return null ; } }
executeUpdate 方法 execute 方法和 executeUpdate 方法的返回值不同:
execute 方法如果返回结果集时则返回 true,更新操作时返回的是 false
executeUpdate 方法会返回更新时收到影响的行数
练习1:通过控制台插入数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public class test2 { public static void main (String[] args) throws Exception { Scanner scanner = new Scanner(System.in); System.out.println("请输入name" ); String name = scanner.next(); System.out.println("请输入email" ); String email = scanner.next(); System.out.println("请输入birth" ); String birth = scanner.next(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd" ); Date parse = sdf.parse(birth); java.sql.Date sqlbirth = new java.sql.Date(parse.getTime()); Connection conn = JdbcUtils.getConnection(); String sql = "insert into customers(name,email,birth) values (?,?,?)" ; PreparedStatement pst = conn.prepareStatement(sql); pst.setObject(1 ,name); pst.setObject(2 ,email); pst.setObject(3 ,sqlbirth); if (pst.executeUpdate() > 0 ){ System.out.println("更新成功" ); }else { System.out.println("更新失败" ); } JdbcUtils.closeConnection(conn,pst); } }
练习2:输入学生身份证号查询信息 首先封装好一个通用的查询方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 public static <T> ArrayList<T> executQuery (Class<T> clazz,String sql,Object ...arg) { Connection conn = null ; PreparedStatement pst = null ; ResultSet resultSet = null ; ArrayList<T> ts = new ArrayList<>(); try { conn = JdbcUtils.getConnection(); pst = conn.prepareStatement(sql); for (int i = 0 ; i < arg.length; i++) { pst.setObject(i+1 ,arg[i]); } resultSet = pst.executeQuery(); ResultSetMetaData metaData = resultSet.getMetaData(); int columnCount = metaData.getColumnCount(); while (resultSet.next()){ T t = clazz.newInstance(); for (int i = 0 ; i < columnCount; i++) { Object value = resultSet.getObject(i + 1 ); String label = metaData.getColumnLabel(i + 1 ); Field field = clazz.getDeclaredField(label); field.setAccessible(true ); field.set(t,value); } ts.add(t); } return ts; } catch (Exception e) { e.printStackTrace(); } finally { JdbcUtils.closeConnection(conn,pst,resultSet); } return null ; }
创建一个 Examstudent 类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 public class Examstudent { int FlowID; int Type; String IDCard; String ExamCard; String StudentName; String Location; int Grade; public Examstudent () { } public Examstudent (int flowID, int type, String IDCard, String examCard, String studentName, String location, int grade) { FlowID = flowID; Type = type; this .IDCard = IDCard; ExamCard = examCard; StudentName = studentName; Location = location; Grade = grade; } public int getFlowID () { return FlowID; } public void setFlowID (int flowID) { FlowID = flowID; } public int getType () { return Type; } public void setType (int type) { Type = type; } public String getIDCard () { return IDCard; } public void setIDCard (String IDCard) { this .IDCard = IDCard; } public String getExamCard () { return ExamCard; } public void setExamCard (String examCard) { ExamCard = examCard; } public String getStudentName () { return StudentName; } public void setStudentName (String studentName) { StudentName = studentName; } public String getLocation () { return Location; } public void setLocation (String location) { Location = location; } public int getGrade () { return Grade; } public void setGrade (int grade) { Grade = grade; } @Override public String toString () { return "Examstudent{" + "FlowID=" + FlowID + ", Type=" + Type + ", IDCard='" + IDCard + '\'' + ", ExamCard='" + ExamCard + '\'' + ", StudentName='" + StudentName + '\'' + ", Location='" + Location + '\'' + ", Grade=" + Grade + '}' ; } }
编写测试方法
1 2 3 4 5 6 7 8 9 10 11 public class test4 { public static void main (String[] args) { String sql = "select * from examstudent where IDCard = ?" ; System.out.println("请输入身份证号" ); Scanner sc = new Scanner(System.in); String IDCard = sc.next(); ArrayList<Examstudent> examstudents = JdbcUtils.executQuery(Examstudent.class, sql, IDCard); System.out.println(examstudents); } }
练习3:输入学号删除学生信息 如果输入的学号有误,则提示删除失败重新输入。输入成功则结束程序。
1 2 3 4 5 6 7 8 9 10 11 System.out.println("请输入学生学号" ); Scanner sc = new Scanner(System.in); String sql = "delete from examstudent where FlowID = ?" ; String flowId = sc.next(); int i = JdbcUtils.executeSql(sql,flowId);if (i > 0 ){ System.out.println("删除成功" ); break ; }else { System.out.println("删除失败" ); }
运行效果:
插入 Blob 数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public static void main (String[] args) throws Exception { String sql = "INSERT INTO customers(`name`,email,birth,photo) VALUES(?,?,?,?)" ; FileInputStream file = new FileInputStream(new File("main/src/1.jpg" )); Connection conn = JdbcUtils.getConnection(); PreparedStatement pst = conn.prepareStatement(sql); pst.setObject(1 ,"张三" ); pst.setObject(2 ,"250155555@qq.com" ); pst.setObject(3 ,"1995-05-25" ); pst.setBlob(4 ,file); pst.executeUpdate(); JdbcUtils.closeConnection(conn,pst); }
读取一个Blob数据并保存到本地 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 public class exer02 { public static void main (String[] args) { Connection conn = null ; PreparedStatement pst = null ; ResultSet resultSet = null ; try { conn = JdbcUtils.getConnection(); String sql = "select photo from customers where id = ?" ; pst = conn.prepareStatement(sql); pst.setObject(1 ,27 ); resultSet = pst.executeQuery(); if (resultSet.next()){ Blob photo = resultSet.getBlob(1 ); InputStream bs = photo.getBinaryStream(); FileOutputStream fos = new FileOutputStream("2.jpg" ); byte [] bytes = new byte [1024 ]; int len; while ((len = bs.read(bytes)) != -1 ){ fos.write(bytes,0 ,len); } bs.close(); fos.close(); } } catch (Exception e) { e.printStackTrace(); } finally { JdbcUtils.closeConnection(conn,pst,resultSet); } } }
运行代码后自动生成 2.jpg 文件
数据库的二进制类型大小
类型
大小
TinyBlob
最大 255 b
Blob
最大 65KB
MediumBlob
最大 16M
LongBlob
最大 4G
插入blob数据的特殊情况 在数据库中我们设置了字段类型为 mediumblob ,表示最大可以存储16M的文件,但是当我们插入一个超过1M的数据时会出现下面的错误:
我们插入一个大小为 8M 的ppt
1 2 3 4 5 public static void main (String[] args) throws FileNotFoundException { String sql = "INSERT INTO customers(photo) VALUES (?)" ; FileInputStream fis = new FileInputStream(new File("main/src/1.pptx" )); JdbcUtils.executeSql(sql, fis); }
上述错误表示我们当前一次性插入的数据过大,这时我们要去修改 MySQL 的安装目录下的 my.ini 文件,添加如下内容。表示一次上传内容大小最大修改为 16M
之后一定要重启 MySQL 服务,然后再次运行程序,错误消失
查看数据库,成功插入其中
批量插入数据 方式一: prepareStatement 方法会提前预编译 SQL 语句,当批量插入时,会对同一 SQL 语句缓存,只需要传入占位值就可以执行对应的 SQL 语句
编辑代码,批量插入 5000 条数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public static void main (String[] args) { Connection conn = null ; PreparedStatement pst = null ; try { long strat = System.currentTimeMillis(); String sql = "INSERT INTO goods(good_name) VALUES(?)" ; conn = JdbcUtils.getConnection(); pst = conn.prepareStatement(sql); for (int i = 0 ; i < 5000 ; i++) { pst.setObject(1 ,"name_" +i+1 ); pst.executeUpdate(); } long end = System.currentTimeMillis(); System.out.println("花费时长:" + (end - strat)); } catch (Exception e) { e.printStackTrace(); } finally { JdbcUtils.closeConnection(conn,pst); } }
方式二: 使用 addBatch() / executeBatch() / clearBatch()
MySQL 默认关闭批处理功能,我们需要开启这个功能。在MySQL连接地址后面添加 ?rewriteBatchedStatements=true
编辑代码,批量插入 1000000 条数据,花费:7839
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 public static void main (String[] args) { Connection conn = null ; PreparedStatement pst = null ; try { long strat = System.currentTimeMillis(); String sql = "INSERT INTO goods(good_name) VALUES(?)" ; conn = JdbcUtils.getConnection(); pst = conn.prepareStatement(sql); for (int i = 0 ; i < 1000000 ; i++) { pst.setObject(1 ,"name_" +i+1 ); pst.addBatch(); if (i % 500 == 0 ){ pst.executeBatch(); pst.clearBatch(); } } long end = System.currentTimeMillis(); System.out.println("花费时长:" + (end - strat)); } catch (Exception e) { e.printStackTrace(); } finally { JdbcUtils.closeConnection(conn,pst); } }
方式三:最终版
首先设置 conn.setAutoCommit(false);
手动提交 conn.commit();
还是插入 1000000 数据,花费:5733
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public static void main (String[] args) { Connection conn = null ; PreparedStatement pst = null ; try { long strat = System.currentTimeMillis(); conn = JdbcUtils.getConnection(); conn.setAutoCommit(false ); String sql = "INSERT INTO goods(good_name) VALUES(?)" ; pst = conn.prepareStatement(sql); for (int i = 1 ; i <= 1000000 ; i++) { pst.setObject(1 ,"name" + i); pst.addBatch(); if (i % 500 == 0 ){ pst.executeBatch(); pst.clearBatch(); } } conn.commit(); long end = System.currentTimeMillis(); System.out.println("花费时长:" + (end - strat)); } catch (Exception e) { e.printStackTrace(); } finally { JdbcUtils.closeConnection(conn,pst); } }
事务 什么是事务
事务:一组逻辑操作单元,使数据从一种状态变换成另外一种状态
事务处理:保证所有的事务都作为一个工作单元来执行,即使出现了故障,都不能改变这种执行方式。当在一个事务中执行多个操作时,要么所有的事务都被提交,那么这些修改就被永久的保存下来。要么数据库管理系统将放弃所做的所有修改,整个事务回滚到最初状态
通过案例引出事务 场景:账户A需要往账户B转100元,如果中间程序出错,使用下面的代码会出现什么问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public static void main (String[] args) { try { String sql1 = "UPDATE user_table SET balance = balance - 100 WHERE `user` = ?" ; JdbcUtils.executeSql(sql1,"AA" ); System.out.println(10 /0 ); String sql2 = "UPDATE user_table SET balance = balance + 100 WHERE `user` = ?" ; JdbcUtils.executeSql(sql2,"BB" ); } catch (Exception e) { e.printStackTrace(); } }
运行后AA账户少了100,但是BB账户没有增加
这时我们就要用到事务,事务的作用就是在同时执行多个SQL时,如果有一条执行失败,则全部SQL都执行失败
事务处理的原则 当一个事务执行多个操作时,要么所有事务都成功,否则会被回滚到最初状态
那些操作会自动提交
DDL 操作一旦执行,都会自动提交
DML 默认情况下会自动提交
可以通过 set autocommit = false 来关闭自动提交
默认关闭连接时,回自动提交
使用事务处理转账问题 首先封装一个考虑了事务的通用执行SQL方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public static int executeSql (Connection conn,String sql,Object ...arg) throws Exception { PreparedStatement pst = conn.prepareStatement(sql); for (int i = 0 ; i < arg.length; i++) { pst.setObject(i+1 ,arg[i]); } int res = pst.executeUpdate(); JdbcUtils.closeConnection(null ,pst); return res; }
编写代码处理转账问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 public static void main (String[] args) { Connection conn = null ; try { conn = JdbcUtils.getConnection(); conn.setAutoCommit(false ); String sql1 = "UPDATE user_table SET balance = balance - 100 WHERE `user` = ?" ; JdbcUtils.executeSql(conn,sql1,"AA" ); System.out.println(10 / 0 ); String sql2 = "UPDATE user_table SET balance = balance + 100 WHERE `user` = ?" ; JdbcUtils.executeSql(conn,sql2,"BB" ); conn.commit(); } catch (Exception e) { e.printStackTrace(); try { conn.rollback(); } catch (SQLException throwables) { throwables.printStackTrace(); } } finally { try { JdbcUtils.closeConnection(conn,null ); } catch (Exception e) { e.printStackTrace(); } } }
执行上面的代码后,再来查看数据库是否发生变化
可以看到数据没有发生改变,说明我们事务生效
关闭连接时将连接恢复为默认值
事务的ACID属性
原子性
原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生
一致性
隔离性
事务的隔离性是指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰
持久性
持久性是指一个事务一旦被提交,他对数据库中的变化就是永久的,接下来的其他操作和数据库故障不应该对其有任何影响
四种隔离机制
四种并发问题
脏读:对于两个事务T1,T2,T1 读取了已经被T2更新但是还未提交的数据字段。之后,若T2回滚,T1读取的内容就是无效且临时的
不可重复读:对于两个事务T1,T2,T1读取了一个字段,然后T2更新了该字段。之后T1再次读取了同一个字段,两次读取的值不同
幻读:对于两个事务T1,T2。T1从一个表中读取了一个字段,T2在这个表中插入了一些新的行,之后,如果T1再读取这个表就会多出几行
数据库事务的隔离性:数据库系统必须具有隔离并发运行各个事务的能力,使他们不会互相影响,避免各种并发问题
一个事务与其他事务的隔离程度称为隔离级别,数据库规定了多种隔离级别,不同的隔离级别对应不同的干扰程度,隔离级别越高,数据的一致性就越好,但是并发性越弱。
数据库的四种隔离级别
隔离级别
描述
READ UNCOMMTTED(读取未提交数据)
允许事务读取未被其他事务提交的变更,脏读,不可重读和幻读问题都会出现
READ COMMITED(读已提交的数据)
只允许事务读取已被其他事务提交的变更,可以避免脏读,但不可重读和幻读问题都会出现
REPEATABLE READ(可重复读)
确保事务可以多次从一个字段中读取相同的值,在这个事务持续之间,禁止其他事务对这个字段进行更新,可以避免脏读和不可重复读,但是幻读问题仍会存在
SERIALLIZABLE(串行化)
确保事务可以从一个表中读取相同的行,在这个事务持续期间,禁止其他事务对该表执行插入,更新和删除操作,所有并发问题都会得到解决,但是性能十分低下
通过Java代码体会隔离机制 读取未提交数据 首先新建一个查询方法,查询方法中我们手动的将隔离等级设置为读取未提交数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @Test public void testSelect () throws Exception { Connection conn = JdbcUtils.getConnection(); int transactionIsolation = conn.getTransactionIsolation(); System.out.println(transactionIsolation); conn.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED); conn.setAutoCommit(false ); String sql = "select * from user_table where user = ?" ; ArrayList<UserTable> cc = JdbcUtils.executQuery(conn, UserTable.class, sql, "CC" ); System.out.println(cc); conn.close(); }
接着新建一个更新方法,更新方法里面添加一个15秒的睡眠
1 2 3 4 5 6 7 8 9 10 @Test public void testUpdate () throws Exception { Connection conn = JdbcUtils.getConnection(); conn.setAutoCommit(false ); String sql = "update user_table set balance = ? where user = ?" ; JdbcUtils.executeSql(conn, sql, 5000 , "CC" ); Thread.sleep(15000 ); System.out.println("修改成功" ); }
然后首先运行查询方法,查询到的用户CC的balance的值等于3000
然后运行更新方法,更新方法取消了自动提交,所以更新数据不会被提交到数据库中,执行之后进入15秒的睡眠
在更新方法的睡眠期间再次执行查询方法,此时可以看到查询到的结果变成5000,体现了隔离等级为读取未提交数据
当更新方法睡眠结束后再次执行查询方法,结果又变回了3000
读取已提交 接着上面的代码修改
首先修改更新方法,设置修改后立即提交数据
1 2 3 4 5 6 7 8 9 10 @Test public void testUpdate () throws Exception { Connection conn = JdbcUtils.getConnection(); conn.setAutoCommit(false ); String sql = "update user_table set balance = ? where user = ?" ; JdbcUtils.executeSql(conn, sql, 5000 , "CC" ); conn.commit(); System.out.println("修改成功" ); }
然后再查询方法中添加一个10秒中的睡眠,睡眠结束后再次查询数据,实现在一个事务中执行两次查询操作。
同时将隔离等级设置为 读取已提交数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 @Test public void testSelect () throws Exception { Connection conn = JdbcUtils.getConnection(); int transactionIsolation = conn.getTransactionIsolation(); System.out.println(transactionIsolation); conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED); conn.setAutoCommit(false ); String sql = "select * from user_table where user = ?" ; ArrayList<UserTable> cc = JdbcUtils.executQuery(conn, UserTable.class, sql, "CC" ); System.out.println(cc); System.out.println("开始睡眠" ); Thread.sleep(15000 ); System.out.println("睡眠结束" ); ArrayList<UserTable> cc1 = JdbcUtils.executQuery(conn, UserTable.class, sql, "CC" ); System.out.println(cc1); conn.close(); }
接着首先执行查询方法,在查询方法执行期间再次执行一下更新方法,观察运行结果
通过结果可以看到在一次查询事务中,两次查询到的结果不同,这种现象称之为幻读
不可重复读(MySQL默认) 接着上面的代码修改,在查询方法中将隔离等级设置为 TRANSACTION_REPEATABLE_READ,MySQL默认是这个等级
1 conn.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ);
然后观察运行结果
通过结果可以看到虽然修改了数据库中的值,但是在一个事务中两次查询到的值是一样的。这种称为不可重复读
封装查询方法和测试 BaseDao 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 package com.songzx.dao;import com.songzx.util.JdbcUtils;import java.lang.reflect.Field;import java.sql.*;import java.util.ArrayList;public class BaseDao { public int executeSql (Connection conn, String sql, Object ...arg) throws Exception { PreparedStatement pst = conn.prepareStatement(sql); for (int i = 0 ; i < arg.length; i++) { pst.setObject(i+1 ,arg[i]); } int res = pst.executeUpdate(); JdbcUtils.closeConnection(null ,pst); return res; } public <T> T returnOneData (Connection conn, Class<T> clazz, String sql, Object ...arg) { PreparedStatement pst = null ; ResultSet resultSet = null ; try { pst = conn.prepareStatement(sql); for (int i = 0 ; i < arg.length; i++) { pst.setObject(i+1 ,arg[i]); } resultSet = pst.executeQuery(); ResultSetMetaData metaData = resultSet.getMetaData(); int count = metaData.getColumnCount(); if (resultSet.next()){ T t = clazz.newInstance(); for (int i = 0 ; i < count; i++) { Object value = resultSet.getObject(i + 1 ); String label = metaData.getColumnLabel(i + 1 ); Field field = clazz.getDeclaredField(label); field.setAccessible(true ); field.set(t,value); } return t; } } catch (Exception e) { e.printStackTrace(); } finally { try { JdbcUtils.closeConnection(null ,pst,resultSet); } catch (SQLException throwables) { throwables.printStackTrace(); } } return null ; } public <T> ArrayList<T> executQuery (Connection conn,Class<T> clazz,String sql,Object ...arg) throws SQLException { PreparedStatement pst = null ; ResultSet resultSet = null ; ArrayList<T> ts = new ArrayList<>(); try { pst = conn.prepareStatement(sql); for (int i = 0 ; i < arg.length; i++) { pst.setObject(i+1 ,arg[i]); } resultSet = pst.executeQuery(); ResultSetMetaData metaData = resultSet.getMetaData(); int columnCount = metaData.getColumnCount(); while (resultSet.next()){ T t = clazz.newInstance(); for (int i = 0 ; i < columnCount; i++) { Object value = resultSet.getObject(i + 1 ); String label = metaData.getColumnLabel(i + 1 ); Field field = clazz.getDeclaredField(label); field.setAccessible(true ); field.set(t,value); } ts.add(t); } return ts; } catch (Exception e) { e.printStackTrace(); } finally { JdbcUtils.closeConnection(null ,pst,resultSet); } return null ; } public <E> E getValue (Connection conn, String sql, Object ...arg) { PreparedStatement pst = null ; ResultSet resultSet = null ; try { pst = conn.prepareStatement(sql); for (int i = 0 ; i < arg.length; i++) { pst.setObject(i+1 ,arg[i]); } resultSet = pst.executeQuery(); if (resultSet.next()){ return (E) resultSet.getObject(1 ); } } catch (SQLException throwables) { throwables.printStackTrace(); } finally { try { JdbcUtils.closeConnection(null ,pst,resultSet); } catch (SQLException throwables) { throwables.printStackTrace(); } } return null ; } }
CustomerDao 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 package com.songzx.dao;import com.songzx.bean.Customers;import java.sql.Connection;import java.sql.Date;import java.sql.SQLException;import java.util.List;public interface CustomerDao { void inset (Connection conn, Customers cus) throws Exception ; void deleteById (Connection conn,int id) throws Exception ; void updateCusById (Connection conn,Customers cus) throws Exception ; Customers selectCusById (Connection conn,int id) ; List<Customers> selectAllCus (Connection conn) throws SQLException ; long getCount (Connection conn) ; Date getMaxBirth (Connection conn) ; }
CustomerImpDao 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 package com.songzx.dao;import com.songzx.bean.Customers;import java.sql.Connection;import java.sql.Date;import java.sql.SQLException;import java.util.ArrayList;import java.util.List;public class CustomerImpDao extends BaseDao implements CustomerDao { @Override public void inset (Connection conn, Customers cus) throws Exception { String sql = "insert into customers(name,email,birth) values(?,?,?)" ; executeSql(conn,sql,cus.getName(),cus.getEmail(),cus.getBirth()); } @Override public void deleteById (Connection conn, int id) throws Exception { String sql = "delete from customers where id = ?" ; executeSql(conn,sql,id); } @Override public void updateCusById (Connection conn, Customers cus) throws Exception { String sql = "update customers set name = ?,email = ?,birth = ? where id = ?" ; executeSql(conn,sql,cus.getName(),cus.getEmail(),cus.getBirth(),cus.getId()); } @Override public Customers selectCusById (Connection conn, int id) { String sql = "select id,name,email,birth from customers where id = ?" ; Customers customers = returnOneData(conn, Customers.class, sql, id); return customers; } @Override public List<Customers> selectAllCus (Connection conn) throws SQLException { String sql = "select name,email,birth from customers" ; ArrayList<Customers> customers = executQuery(conn, Customers.class, sql); return customers; } @Override public long getCount (Connection conn) { String sql = "select count(*) from customers" ; long count = (Long) getValue(conn, sql); return count; } @Override public Date getMaxBirth (Connection conn) { String sql = "select max(birth) from customers" ; Date date = (Date) getValue(conn, sql); return date; } }
CustomerDaoTest 添加测试方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 package com.songzx.daotest;import com.songzx.bean.Customers;import com.songzx.dao.CustomerImpDao;import com.songzx.util.JdbcUtils;import java.sql.Connection;import java.sql.Date;import java.text.SimpleDateFormat;import java.util.List;public class CustomerDaoTest { public static void main (String[] args) throws Exception { Connection conn = JdbcUtils.getConnection(); CustomerImpDao dao = new CustomerImpDao(); Customers customers = dao.selectCusById(conn, 6 ); System.out.println(customers); System.out.println("******" ); List<Customers> customers1 = dao.selectAllCus(conn); System.out.println(customers1); System.out.println("******" ); long count = dao.getCount(conn); System.out.println(count); System.out.println("******" ); Date maxBirth = dao.getMaxBirth(conn); System.out.println(maxBirth); System.out.println("******" ); customers.setName("王菲" ); System.out.println(customers); dao.updateCusById(conn,customers); System.out.println("******" ); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-HH-dd" ); java.util.Date parse = sdf.parse("2012-05-06" ); Date yybirth = new Date(parse.getTime()); Customers yscus = new Customers(30 , "易烊千玺" , "yyqx@163.com" , yybirth); dao.inset(conn,yscus); conn.close(); } }
数据库连接池
为解决传统开发中数据库连接问题,可以采用数据库连接池技术
数据库连接池的基本思想 :就是为数据库连接建立一个缓冲池,预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需要从池子中取出一个用,使用完毕后放回去
数据库连接池 负责分配,管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是重新新建一个
c3p0 首先添加驱动
连接方式一
1 2 3 4 5 6 7 8 9 10 11 12 @Test public void c3p0test1 () throws PropertyVetoException, SQLException { ComboPooledDataSource cpds = new ComboPooledDataSource(); cpds.setDriverClass( "com.mysql.cj.jdbc.Driver" ); cpds.setJdbcUrl( "jdbc:mysql://localhost:3306/jdbc_learn" ); cpds.setUser("root" ); cpds.setPassword("abc123" ); cpds.setInitialPoolSize(15 ); Connection conn = cpds.getConnection(); System.out.println(conn); }
连接方式二
首先新建xml格式的配置文件,文件名必须为:c3p0-config.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 <?xml version="1.0" encoding="UTF-8" ?> <c3p0-config > <named-config name ="intergalactoApp" > <property name ="driverClass" > com.mysql.cj.jdbc.Driver</property > <property name ="jdbcUrl" > jdbc:mysql://localhost:3306/jdbc_learn</property > <property name ="user" > root</property > <property name ="password" > abc123</property > <property name ="acquireIncrement" > 50</property > <property name ="initialPoolSize" > 100</property > <property name ="minPoolSize" > 50</property > <property name ="maxPoolSize" > 1000</property > <property name ="maxStatements" > 0</property > <property name ="maxStatementsPerConnection" > 5</property > </named-config > </c3p0-config >
编写代码连接
1 2 3 4 5 6 7 8 9 10 @Test public void c3p0test2 () { ComboPooledDataSource cpds = new ComboPooledDataSource("intergalactoApp" ); try { Connection conn = cpds.getConnection(); System.out.println(conn); } catch (SQLException throwables) { throwables.printStackTrace(); } }
dbcp 首先导入两个包
Commons-dbcp.jar:连接池的实现
Commons-pool.jar:连接池实现的依赖库
配置文件说明
属性
默认值
说明
initialSize
0
连接池启动时创建的初始化连接数量
maxActive
8
连接池中可同时连接的最大的连接数
maxIdle
8
连接池中最大的空闲的连接数,超过的空闲连接将被释放,如果设置为负数表示不限制
minIdle
0
连接池中最小的空闲的连接数,低于这个数量会被创建新的连接。该参数越接近maxIdle,性能越好,因为连接的创建和销毁,都是需要消耗资源的;但是不能太大。
maxWait
无限制
最大等待时间,当没有可用连接时,连接池等待连接释放的最大时间,超过该时间限制会抛出异常,如果设置-1表示无限等待
poolPreparedStatements
false
开启池的Statement是否prepared
maxOpenPreparedStatements
无限制
开启池的prepared 后的同时最大连接数
minEvictableIdleTimeMillis
连接池中连接,在时间段内一直空闲, 被逐出连接池的时间
removeAbandonedTimeout
300
超过时间限制,回收没有用(废弃)的连接
removeAbandoned
false
超过removeAbandonedTimeout时间后,是否进 行没用连接(废弃)的回收
连接方式一:
1 2 3 4 5 6 7 8 9 10 11 12 13 @Test public void test1 () throws SQLException { BasicDataSource source = new BasicDataSource(); source.setDriverClassName("com.mysql.cj.jdbc.Driver" ); source.setUrl("jdbc:mysql://127.0.0.1:3306/jdbc_learn" ); source.setUsername("root" ); source.setPassword("abc123" ); source.setInitialSize(10 ); Connection conn = source.getConnection(); System.out.println(conn); }
连接方式二:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 private static DataSource source = null ;static { try { Properties props = new Properties(); FileInputStream fis = new FileInputStream("main/lib/dbcp.properties" ); props.load(fis); source = BasicDataSourceFactory.createDataSource(props); } catch (Exception e) { e.printStackTrace(); } } public static Connection getDbcpConnection () throws SQLException { Connection conn = source.getConnection(); return conn; }
dbcp.properties
配置文件如下:
1 2 3 4 username =root password =abc123 url =jdbc:mysql://127.0.0.1:3306/jdbc_learn?rewriteBatchedStatements=true driverClassName =com.mysql.cj.jdbc.Driver
Druid (常用的连接池技术) 首先导入配置文件
配置信息
配置
缺省
说明
name
配置这个属性的意义在于,如果存在多个数据源,监控的时候可以通过名字来区分开来。 如果没有配置,将会生成一个名字,格式是:”DataSource-” + System.identityHashCode(this)
url
连接数据库的url,不同数据库不一样。例如:mysql : jdbc:mysql://10.20.153.104:3306/druid2 oracle : jdbc:oracle:thin:@10.20.149.85:1521:ocnauto
username
连接数据库的用户名
password
连接数据库的密码。如果你不希望密码直接写在配置文件中,可以使用ConfigFilter。详细看这里:https://github.com/alibaba/druid/wiki/%E4%BD%BF%E7%94%A8ConfigFilter
driverClassName
根据url自动识别 这一项可配可不配,如果不配置druid会根据url自动识别dbType,然后选择相应的driverClassName(建议配置下)
initialSize
0
初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时
maxActive
8
最大连接池数量
maxIdle
8
已经不再使用,配置了也没效果
minIdle
最小连接池数量
maxWait
获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。
poolPreparedStatements
false
是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭。
maxOpenPreparedStatements
-1
要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100
validationQuery
用来检测连接是否有效的sql,要求是一个查询语句。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会其作用。
testOnBorrow
true
申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
testOnReturn
false
归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能
testWhileIdle
false
建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
timeBetweenEvictionRunsMillis
有两个含义: 1)Destroy线程会检测连接的间隔时间2)testWhileIdle的判断依据,详细看testWhileIdle属性的说明
numTestsPerEvictionRun
不再使用,一个DruidDataSource只支持一个EvictionRun
minEvictableIdleTimeMillis
connectionInitSqls
物理连接初始化的时候执行的sql
exceptionSorter
根据dbType自动识别 当数据库抛出一些不可恢复的异常时,抛弃连接
filters
属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有: 监控统计用的filter:stat日志用的filter:log4j防御sql注入的filter:wall
proxyFilters
类型是List,如果同时配置了filters和proxyFilters,是组合关系,并非替换关系
使用方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public static DataSource druidSource = null ;static { try { Properties props = new Properties(); FileInputStream fis = new FileInputStream("main/lib/Druid.properties" ); props.load(fis); druidSource = DruidDataSourceFactory.createDataSource(props); } catch (Exception e) { e.printStackTrace(); } } public static Connection getDruidConnection () throws Exception { Connection conn = druidSource.getConnection(); return conn; }
Druid.properties
配置文件
1 2 3 4 username =root password =abc123 url =jdbc:mysql://127.0.0.1:3306/jdbc_learn driverClassName =com.mysql.cj.jdbc.Driver
Apache-DBUtils实现CRUD操作 导入包 进入官网:https://commons.apache.org/proper/commons-dbutils/download_dbutils.cgi
选择相关文件进行下载
下载后解压打开,复制 commons-dbutils-1.7.jar
文件导入到项目中
使用 QueryRunner 完成插入操作 使用 runner.update
方法完成增、删、改 操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public static void main (String[] args) { Connection conn = null ; try { QueryRunner runner = new QueryRunner(); conn = JdbcUtils.getDruidConnection(); String sql = "insert into customers(name,email,birth) values (?,?,?)" ; int update = runner.update(conn, sql, "蔡旭混" , "caixukun@163.com" , "2000-05-06" ); if (update > 0 ){ System.out.println("插入了" + update + "条数据" ); }else { System.out.println("插入失败" ); } } catch (Exception e) { e.printStackTrace(); } finally { try { JdbcUtils.closeConnection(conn,null ); } catch (SQLException throwables) { throwables.printStackTrace(); } } }
ResultSetHandler接口及实现类
查询方法,返回 BeanHandler BeanHandler 实现类返回单个对象
查询 id 等于 10 的数据:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public static void main (String[] args) { Connection conn = null ; try { QueryRunner runner = new QueryRunner(); conn = JdbcUtils.getDruidConnection(); String sql = "select id,`name`,email,birth from customers where id = ?" ; BeanHandler<Customers> cushandler = new BeanHandler<Customers>(Customers.class); Customers cus = runner.query(conn, sql, cushandler, 10 ); System.out.println(cus); } catch (Exception e) { e.printStackTrace(); } finally { try { JdbcUtils.closeConnection(conn,null ); } catch (SQLException throwables) { throwables.printStackTrace(); } } }
查询方法,返回 BeanListHandler BeanListHandler 返回由对象组成的一个集合
查询 id 小于 10 的数据:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public static void main (String[] args) { Connection conn = null ; try { QueryRunner runner = new QueryRunner(); conn = JdbcUtils.getDruidConnection(); String sql = "select id,`name`,email,birth from customers where id < ?" ; BeanListHandler<Customers> beanlist = new BeanListHandler<Customers>(Customers.class); List<Customers> customersList = runner.query(conn, sql, beanlist, 10 ); customersList.forEach(System.out::println); } catch (Exception e) { e.printStackTrace(); } finally { try { JdbcUtils.closeConnection(conn,null ); } catch (SQLException throwables) { throwables.printStackTrace(); } } }
查询方法,返回 MapHandler 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public static void main (String[] args) { Connection conn = null ; try { QueryRunner runner = new QueryRunner(); conn = JdbcUtils.getDruidConnection(); MapHandler mapHandler = new MapHandler(); String sql = "select id,`name`,email,birth from customers where id = ?" ; Map<String, Object> query = runner.query(conn, sql, mapHandler, 10 ); System.out.println(query); } catch (Exception e) { e.printStackTrace(); } finally { try { JdbcUtils.closeConnection(conn,null ); } catch (SQLException throwables) { throwables.printStackTrace(); } } }
查询方法,返回 MapListHandler 1 2 3 4 5 6 7 8 public static void main (String[] args) throws Exception { QueryRunner runner = new QueryRunner(); Connection conn = JdbcUtils.getDruidConnection(); String sql = "select id,`name`,email,birth from customers where id < ?" ; MapListHandler mapListHandler = new MapListHandler(); List<Map<String, Object>> mapList = runner.query(conn, sql, mapListHandler, 10 ); mapList.forEach(System.out::println); }
查询表中的特殊值 使用 ScalarHandler 查询表中数据总和
1 2 3 4 5 6 7 8 9 public static void main (String[] args) throws Exception { Connection conn = JdbcUtils.getConnection(); QueryRunner runner = new QueryRunner(); String sql = "select count(*) from customers" ; ScalarHandler<Long> handler = new ScalarHandler<>(); Long count = runner.query(conn, sql, handler); System.out.println(count); }
使用 ScalarHandler 查询表中最大的生日
1 2 3 4 5 6 7 8 public static void main (String[] args) throws Exception { QueryRunner runner = new QueryRunner(); Connection conn = JdbcUtils.getDruidConnection(); ScalarHandler<Date> handler = new ScalarHandler<>(); String sql = "select max(birth) from customers" ; Date maxbirth = runner.query(conn, sql, handler); System.out.println(maxbirth); }
使用DbUtils工具类关闭连接 closeQuietly 方法帮我们处理了异常和非空判断
1 2 3 4 public static void dbUtilsClose (Connection conn, Statement pst) { DbUtils.closeQuietly(conn); DbUtils.closeQuietly(pst); }