My day is done, and I am like a boat drawn on the beach, listening to the dance-music of the tide in the evening.
我的白昼已经完了,我象一只泊在海滩上的小船,谛听着晚潮跳舞的乐声。
JDBC基本使用 Java数据库连接,(Java Database Connectivity,简称JDBC)是Java语言中用来规范客户端程序如何来访问数据库的应用程序接口 ,提供了诸如查询和更新数据库中数据的方法。JDBC也是Sun Microsystems的商标。我们通常说的JDBC是面向关系型数据库的。
导入mysql-connector-java-版本.jar
,然后将jar包添加入项目库中
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 public static void main (String[] args) throws ClassNotFoundException, SQLException { Class.forName("com.mysql.cj.jdbc.Driver" ); String url = "jdbc:mysql://localhost:端口/数据库名?useSSL=false&serverTimezone=UTC&characterEncoding=utf8" ; String username = "用户名" ; String password = "密码" ; Connection connection = DriverManager.getConnection(url,username,password); Statement statement = connection.createStatement(); String sql = "SELECT * FROM 数据库名.表名" ; ResultSet resultSet = statement.executeQuery(sql); while (resultSet.next()){ System.out.println("variable=" + resultSet.getObject("列名" )); } resultSet.close(); statement.close(); connection.close(); }
驱动 com.mysql.cj.jdbc.Driver
部分源码
1 2 3 4 5 6 7 8 9 10 11 public class Driver extends NonRegisteringDriver implements java .sql .Driver { public Driver () throws SQLException { } static { try { DriverManager.registerDriver(new Driver()); } catch (SQLException var1) { throw new RuntimeException("Can't register driver!" ); } } }
1 2 3 4 Class.forName("com.mysql.cj.jdbc.Driver" );
URL 1 2 3 4 5 String url = "jdbc:mysql://主机地址:3306/数据库名?useSSL=false&serverTimezone=UTC&characterEncoding=utf8" ; mysql -- 3306 oralce -- 1521 jdbc:oralce:thin:@localhost :1521 :sid
Connection 1 Connection connection = DriverManager.getConnection(url,username,password);
connection
代表数据库,是数据库的对象
1 2 3 connection.rollback(); connection.commit(); connection.setAutoCommit();
Statement 不能防止sql注入点击跳转PreparedStatement
statement
具体的执行类,是执行sql的对象
1 2 3 statement.executeQuery(); statement.execute(); statement.executeUpdate();
ResultSet resultSet
查询的结果集,封装了所有的结果集
1 2 3 4 5 6 7 8 9 resultSet.getObject(); resultSet.getString(); resultSet.getInt(); resultSet.beforeFirst(); resultSet.afterLast(); resultSet.next(); resultSet.previous(); resultSet.absolute(row);
释放资源 1 2 3 resultSet.close(); statement.close(); connection.close();
提取工具类 创建db.properties
配置文件
1 2 3 4 driver = com.mysql.cj.jdbc.Driver url = jdbc:mysql://localhost:端口/数据库名?useSSL=false&serverTimezone=UTC&characterEncoding=utf8&useUnicode=true username = 用户名 password = 密码
通过反射获取类加载器来拿到资源
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 import java.io.IOException;import java.io.InputStream;import java.sql.*;import java.util.Properties;public class JdbcUtils { private static String driver = null ; private static String url = null ; private static String username = null ; private static String password = null ; static { try { InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties" ); Properties properties = new Properties(); properties.load(in); driver = properties.getProperty("driver" ); url = properties.getProperty("url" ); username = properties.getProperty("username" ); password = properties.getProperty("password" ); Class.forName(driver); }catch (Exception e){ e.printStackTrace(); } } public static Connection getConnection () throws SQLException { return DriverManager.getConnection(url,username,password); } public static void release (Connection conn, Statement st, ResultSet rs) { if (rs!=null ){ try { rs.close(); }catch (SQLException e){ e.printStackTrace(); } } if (st!=null ){ try { st.close(); }catch (SQLException e){ e.printStackTrace(); } } if (conn!=null ){ try { conn.close(); }catch (SQLException e){ e.printStackTrace(); } } } }
Class是当前类的Class对象,Class.getClassLoader()是获取当前类的类加载器。类加载器的大概作用是当需要使用一个类时,加载该类的”.class”文件,并创建对应的class对象,将class文件加载到虚拟机的内存。getResourceAsStream()是获取资源的输入流。类加载器默认是从classPath路径加载资源。
因此,当使用Class.getClassLoader.getResourceAsStream()
加载资源时,是从classPath路径下进行加载,放在resources下的文件加载时不能加(“/”)。
1 InputStream in = PropertiesUtil.class.getClassLoader().getResourceAsStream("xx.properties" );
Class.getResourceAsStream()
1 2 3 4 Class.getResourceAsStream("" ); Class.getResourceAsStream("/" );
在使用 Class.getResourceAsStream()时,一定注意要加载的资源路径与当前类所在包的路径是否一致【使用时注意子目录】。
1)要加载的资源路径与当前类所在包的路径一致
1 InputStream in = PropertiesUtil.class.getResourceAsStream("xx.properties" );
2)要加载的资源路径在resources下
1 InputStream in = PropertiesUtil.class.getResourceAsStream("/xx.properties" );
提取插入类
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 import java.sql.Connection;import java.sql.ResultSet;import java.sql.SQLException;import java.sql.Statement;public class TestInsert { public static void main (String[] args) { Connection conn = null ; Statement st = null ; ResultSet rs = null ; try { conn = JdbcUtils.getConnection(); st = conn.createStatement(); String sql = "插入的sql语句" ; int i = st.executeUpdate(sql); if (i>0 ){ System.out.println("插入成功" ); } } catch (SQLException throwables) { throwables.printStackTrace(); }finally { JdbcUtils.release(conn,st,rs); } } }
SQL注入问题 SQL注入即是指web应用程序 对用户输入数据的合法性没有判断或过滤不严,攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的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 import java.sql.Connection;import java.sql.ResultSet;import java.sql.SQLException;import java.sql.Statement;public class SQL 注入 { public static void main (String[] args) { login("'or'1=1" ,"'or'1=1" ); } public static void login (String username,String password) { Connection conn = null ; Statement st = null ; ResultSet rs = null ; try { conn = JdbcUtils.getConnection(); st = conn.createStatement(); String sql = "select * from users where `NAME`='" + username + "' AND `password` = '" + password + "'" ; rs = st.executeQuery(sql); while (rs.next()){ System.out.println("查询成功" ); System.out.println(rs.getString("NAME" )); System.out.println(rs.getString("password" )); } } catch (SQLException throwables) { throwables.printStackTrace(); }finally { JdbcUtils.release(conn,st,rs); } } }
login()
方法传递进去的参数会被拼接入sql语句中,称为执行sql语句的一部分,使sql语句保持正确的语法,进而被执行;
PreparedStatement 点击跳转Statement
PreparedStatement可以防止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 import com.bobo.lesson02.utils.JdbcUtils;import java.sql.*;public class SQL 注入 { public static void main (String[] args) { login("'' or 1=1" ,"123123" ); } public static void login (String username,String password) { Connection conn = null ; PreparedStatement st = null ; ResultSet rs = null ; try { conn = JdbcUtils.getConnection(); String sql = "select * from users where `NAME`= ? and `password` = ?" ; st = conn.prepareStatement(sql); st.setString(1 ,username); st.setString(2 ,password); rs = st.executeQuery(); while (rs.next()){ System.out.println("查询成功" ); System.out.println(rs.getString("NAME" )); System.out.println(rs.getString("password" )); } } catch (SQLException throwables) { throwables.printStackTrace(); }finally { JdbcUtils.release(conn,st,rs); } } }
login()
传递进来的参数会被当作字符串,只相当于参数,而不是sql执行语句中的一部分,字符串的转义字符会被忽略,无法创造满足的条件。
数据库连接池 池化技术 概念
池化技术:把一些能够复用的东西(比如说数据库连接、线程)放到池中,避免重复创建、销毁的开销,从而极大提高性能。
在开发过程中我们会用到很多的连接池,像是数据库连接池、HTTP 连接池、Redis 连接池等等。而连接池的管理是连接池设计的核心,我就以数据库连接池为例,来说明一下连接池管理的关键点。
数据库连接池
数据库连接池有两个最重要的配置:最小连接数和最大连接数,它们控制着从连接池中获取连接的流程:
如果当前连接数小于最小连接数,则创建新的连接处理数据库请求
如果线程池中有空闲连接,则使用空闲连接
如果没有空闲连接,并且当前连接数小于最大连接数,则继续创建新的连接
如果当前连接数大于等于最大连接数,并且没有空闲连接了,则请求按照超时时间等待旧连接可用。、
超时之后,则获取数据库连接失败
对于数据库连接池,根据我的经验,一般在线上我建议最小连接数控制在 10 左右,最大连接数控制在 20~30 左右即可。
IDEA使用DBCP连接池 导入commons-dbcp-版本号.jar
commons-pool-版本号.jar
,然后将jar包添加入项目库中
DBCP2还需要导入commons-logging-版本号.jar
提取工具类
创建dbcpconfig.properties
配置文件
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 driverClassName = com.mysql.cj.jdbc.Driver url = jdbc:mysql://localhost:端口/数据库名?useSSL=false&serverTimezone=UTC&characterEncoding=utf8&useUnicode=true username = 用户名 password = 密码 initialSize =10 maxActive =50 maxIdle =20 minIdle =5 maxWait =60000 connectionProperties =useUnicode=true;characterEncoding=UTF8 defaultAutoCommit =true defaultReadOnly =defaultTransactionIsolation =READ_UNCOMMITTED
DBCP工具类
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 import org.apache.commons.dbcp2.BasicDataSource;import org.apache.commons.dbcp2.BasicDataSourceFactory;import javax.sql.DataSource;import java.io.InputStream;import java.sql.*;import java.util.Properties;public class JdbcUtils_DBCP { private static DataSource dataSource = null ; static { try { InputStream in = JdbcUtils_DBCP.class.getClassLoader().getResourceAsStream("dbcpconfig.properties" ); Properties properties = new Properties(); properties.load(in); dataSource = BasicDataSourceFactory.createDataSource(properties); }catch (Exception e){ e.printStackTrace(); } } public static Connection getConnection () throws SQLException { return dataSource.getConnection(); } public static void release (Connection conn, Statement st, ResultSet rs) { if (rs!=null ){ try { rs.close(); }catch (SQLException e){ e.printStackTrace(); } } if (st!=null ){ try { st.close(); }catch (SQLException e){ e.printStackTrace(); } } if (conn!=null ){ try { conn.close(); }catch (SQLException e){ e.printStackTrace(); } } } }