JDBC概述
引入连接MySql的jar包
- 前往https://mvnrepository.com/搜索对应版本jar包进行下载。
2. 在工程项目下创建一个lib文件夹用于存放jar包,右键lib文件将将其添加为库。
JDBC基本使用步骤
- 注册驱动
- 获取链接
- 创建发送sql语句对象
- 发送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
| package com.geo.api.statement;
import com.mysql.cj.jdbc.Driver;
import java.sql.*;
public class StatementQueryPart { public static void main(String[] args) throws SQLException { DriverManager.registerDriver(new Driver());
Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/jd", "root", "123456"); Statement statement = connection.createStatement(); String sql = "select * from t_user;"; ResultSet resultSet = statement.executeQuery(sql); while (resultSet.next()){ int id = resultSet.getInt("id"); String account = resultSet.getString("account"); String password = resultSet.getString("password"); String nickname = resultSet.getString("nickname"); System.out.println(id+"-"+account+"-"+password+"-"+nickname); } resultSet.close(); statement.close(); connection.close(); }
}
|
注册驱动存在的问题:
- DriverManager.registerDriver()方法本身会注册一次。
- Driver.static{}静态代码块中也会注册一次。
这就导致了不必要的性能消耗。
解决方案:使用反射,可以更加灵活的通过传进来的字符串来更改数据库的配置文件
Class.forName(“com.mysql.cj.jdbc.Driver”);
SQL分类:
DDL(容器创建、修改、删除)、DML(插入、修改、删除)、DQL(查询)、DCL(权限控制)、TCL(事物控制语句)
参数:sql语句:非DQL
返回:int型
情况1:DML返回影响的行数。比如删除了三行,返回3;插入了两行,返回2;修改了0条,返回0。
情况2:非DML return 0。
int row =executeUpdate(sql)
参数:DQL
返回:resultSet结果封装对象
ResultSet resultSet = statement.executeQuery(sql);
基于statement实现模拟登录中存在的问题
- SQL语句需要进行字符串拼接,比较麻烦。
- 只能拼接字符串类型,其他数据库类型无法处理。
- 可能发生注入攻击:动态值充当了SQL语句结构,影响了原有的查询结果:比如在输入密码的时候输入’ or ‘1’ = ‘1,就会登录成功。
使用preparedstatement进行登录模拟(防止注入攻击)
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
| package com.geo.api.preparedstatement;
import com.mysql.cj.jdbc.Driver;
import java.sql.*; import java.util.Scanner;
public class PreparedstatementUserLogin { public static void main(String[] args) throws ClassNotFoundException, SQLException { String password = ""; String account = ""; Scanner scanner = new Scanner(System.in); System.out.println("请输入账号:"); account = scanner.next(); System.out.println("请输入密码:"); password = scanner.next();
Class.forName("com.mysql.cj.jdbc.Driver");
Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/jd", "root", "123456"); String sql = "select * from t_user where account = ? and password = ? ; "; PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setObject(1, account); preparedStatement.setObject(2, password);
ResultSet resultSet = preparedStatement.executeQuery(); if (resultSet.next()) { System.out.println("登录成功"); } else { System.out.println("登录失败"); } resultSet.close(); preparedStatement.close(); connection.close(); scanner.close(); } }
|
动态实现增删改查操作
插入数据
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
| public void test1() throws ClassNotFoundException, SQLException { String account = ""; String password = ""; String nickname = ""; Scanner scanner = new Scanner(System.in); System.out.println("请输入要添加的账号:"); account = scanner.next(); System.out.println("请输入密码:"); password = scanner.next(); System.out.println("请输入昵称:"); nickname = scanner.next();
Class.forName("com.mysql.cj.jdbc.Driver"); Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/jd", "root", "123456"); String sql = "insert into t_user(account,password,nickname) values(?,?,?);"; PreparedStatement preparedStatement = connection.prepareStatement(sql); preparedStatement.setObject(1, account); preparedStatement.setObject(2, password); preparedStatement.setObject(3, nickname); int i = preparedStatement.executeUpdate(); if (i != 0) { System.out.println("添加成功!"); } preparedStatement.close(); connection.close(); scanner.close();
|
修改数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public void test2() throws ClassNotFoundException, SQLException { Class.forName("com.mysql.cj.jdbc.Driver"); Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/jd", "root", "123456"); String sql = "update t_user set nickname = ? where id = ?;"; PreparedStatement preparedStatement = connection.prepareStatement(sql); preparedStatement.setObject(1, "db"); preparedStatement.setObject(2, 3); int i = preparedStatement.executeUpdate(); if (i != 0) { System.out.println("修改成功!"); } preparedStatement.close(); connection.close(); }
|
删除数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public void test3() throws ClassNotFoundException, SQLException { Class.forName("com.mysql.cj.jdbc.Driver"); Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/jd", "root", "123456"); String sql = "delete from t_user where id = ?;"; PreparedStatement preparedStatement = connection.prepareStatement(sql); preparedStatement.setObject(1, 3); int i = preparedStatement.executeUpdate(); if (i != 0) { System.out.println("删除成功!"); } preparedStatement.close(); connection.close(); }
|
查询数据
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
| public void test4() throws ClassNotFoundException, SQLException { List<Map> list = new ArrayList<>(); Class.forName("com.mysql.cj.jdbc.Driver"); Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/jd", "root", "123456"); String sql = "select * from t_user;"; PreparedStatement preparedStatement = connection.prepareStatement(sql); ResultSet resultSet = preparedStatement.executeQuery(); ResultSetMetaData metaData = resultSet.getMetaData(); int columnCount = metaData.getColumnCount(); while (resultSet.next()) { Map map = new HashMap();
for (int i = 1; i <= columnCount; i++) { Object value = resultSet.getObject(i); String columnLabel = metaData.getColumnLabel(i); map.put(columnLabel, value); } list.add(map); } System.out.println(list); preparedStatement.close(); connection.close(); }
|
JDBC扩展提升
自增长主键回显的实现
- t_user插入一条数据,并且获取数据库自增长的主键
- 创建preparestatement的时候,告知携带回数据库自增长的主键(sql,Statement.RETURN_GENERATED_KEYS)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| public class PSOtherPart { @Test public void test1() throws ClassNotFoundException, SQLException { Class.forName("com.mysql.cj.jdbc.Driver"); Connection connection = DriverManager.getConnection("jdbc:mysql:///jd", "root", "123456"); String sql = "insert into t_user(account,password,nickname) values (?,?,?);"; PreparedStatement preparedStatement = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); preparedStatement.setObject(1, "66667"); preparedStatement.setObject(2, "13511"); preparedStatement.setObject(3, "haha"); int i = preparedStatement.executeUpdate(); if (i != 0) { System.out.println("执行完毕"); ResultSet generatedKeys = preparedStatement.getGeneratedKeys(); generatedKeys.next(); int id = generatedKeys.getInt(1); System.out.println("主键是:" + id); }
preparedStatement.close(); connection.close(); }
|
批量数据插入性能提升
正常插入1万条数据所需要的时间
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public void test2() throws ClassNotFoundException, SQLException { Class.forName("com.mysql.cj.jdbc.Driver"); Connection connection = DriverManager.getConnection("jdbc:mysql:///jd", "root", "123456"); String sql = "insert into t_user(account,password,nickname) values (?,?,?);"; PreparedStatement preparedStatement = connection.prepareStatement(sql); long start = System.currentTimeMillis(); for (int i = 0; i < 10000; i++) { preparedStatement.setObject(1, "66667" + i); preparedStatement.setObject(2, "13511" + i); preparedStatement.setObject(3, "haha" + i); preparedStatement.executeUpdate(); } long end = System.currentTimeMillis(); System.out.println(end - start); preparedStatement.close(); connection.close(); }
|
使用分批次插入1万条数据所用的时间
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public void test3() throws ClassNotFoundException, SQLException { Class.forName("com.mysql.cj.jdbc.Driver"); Connection connection = DriverManager.getConnection("jdbc:mysql:///jd?rewriteBatchedStatements=true", "root", "123456"); String sql = "insert into t_user(account,password,nickname) values (?,?,?)"; PreparedStatement preparedStatement = connection.prepareStatement(sql); long start = System.currentTimeMillis(); for (int i = 0; i < 10000; i++) { preparedStatement.setObject(1, "xxx" + i); preparedStatement.setObject(2, "111" + i); preparedStatement.setObject(3, "haha" + i); preparedStatement.addBatch(); } preparedStatement.executeBatch(); long end = System.currentTimeMillis(); System.out.println(end - start); preparedStatement.close(); connection.close(); }
|
- 可以看到,在本机上面采用原始的插入方法,要花费将近半分钟的时间,但是使用分批次插入则在不到半秒的时间内就完成了,可见其性能提升幅度之大。
- 操作要点:
- 在路径参数中添加rewriteBatchedStatements=true
- sql语句中必须使用values,不能使用value,且后边不可以加分号结束
- 不是执行语句,是批量添加addBatch()
- 都添加完毕之后,同一批量执行executeBatch()
Druid连接池技术的使用
- Druid连接池的使用是为了降低性能消耗
- 前往Maven下载Druid的jar包,拖进lib文件夹中
- 硬编码和软编码两种使用方式,一般使用软编码,更加灵活。
硬编码方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public void test1() throws SQLException { DruidDataSource druidDataSource = new DruidDataSource(); druidDataSource.setUrl("jdbc:mysql://localhost:3306/jd"); druidDataSource.setUsername("root"); druidDataSource.setPassword("123456"); druidDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); druidDataSource.setInitialSize(5); druidDataSource.setMaxActive(10);
DruidPooledConnection connection = druidDataSource.getConnection();
connection.close(); }
|
软编码方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public void test2() throws Exception { Properties properties = new Properties(); InputStream resourceAsStream = DruidUsePart.class.getClassLoader().getResourceAsStream("druid.properties"); properties.load(resourceAsStream); DataSource dataSource = DruidDataSourceFactory.createDataSource(properties); Connection connection = dataSource.getConnection();
connection.close(); }
|
properties文件
1 2 3 4
| driverClassName=com.mysql.cj.jdbc.Driver username=root password=123456 url=jdbc:mysql:///jd
|
全新JDBC使用优化以及工具类的封装
获取连接工具类v1
将重复的步骤封装起来,只将需要的方法暴露出去。
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
| package com.geo.api.jdbcutils;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource; import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.sql.SQLException; import java.util.Properties;
public class JdbcUtils { private static DataSource dataSource = null;
static { Properties properties = new Properties(); InputStream resourceAsStream = JdbcUtils.class.getClassLoader().getResourceAsStream("druid.properties"); try { properties.load(resourceAsStream); } catch (IOException e) { throw new RuntimeException(e); } try { dataSource = DruidDataSourceFactory.createDataSource(properties); } catch (Exception e) { throw new RuntimeException(e); } }
public static Connection getConnection() throws SQLException { return dataSource.getConnection(); }
public static void closeConnection(Connection connection) throws SQLException { connection.close(); } }
|
获取连接工具类v2
因为在数据的事务操作时,Service和Dao方法属于一个事务;利用线程的本地变量,存储连接信息,就可以不用再进行传递参数了。
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
| package com.geo.api.jdbcutils;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource; import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.sql.SQLException; import java.util.Properties;
public class JdbcUtilsV2 { private static DataSource dataSource = null;
private static ThreadLocal<Connection> tl = new ThreadLocal<>();
static { Properties properties = new Properties(); InputStream resourceAsStream = JdbcUtilsV2.class.getClassLoader().getResourceAsStream("druid.properties"); try { properties.load(resourceAsStream); } catch (IOException e) { throw new RuntimeException(e); } try { dataSource = DruidDataSourceFactory.createDataSource(properties); } catch (Exception e) { throw new RuntimeException(e); } }
public static Connection getConnection() throws SQLException { Connection connection = tl.get(); if (connection == null) { connection = dataSource.getConnection(); tl.set(connection);
} return connection; }
public static void closeConnection() throws SQLException { Connection connection = tl.get(); if (connection != null) { tl.remove(); connection.setAutoCommit(true); connection.close(); } connection.close(); } }
|
baseDao概念和非DQL方法封装
上述是将创建连接和关闭连接的方式封装了,本节将中间的操作部分也进行封装,减少代码的重复使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public int executeUpdate(String sql, Object... params) throws SQLException {
Connection connection = JdbcUtilsV2.getConnection(); PreparedStatement preparedStatement = connection.prepareStatement(sql); for (int i = 1; i < params.length; i++) { preparedStatement.setObject(1, params[i - 1]); } int rows = preparedStatement.executeUpdate(); if (rows != 0) { System.out.println("修改成功!"); } if (connection.getAutoCommit()) { JdbcUtilsV2.closeConnection(); } return rows; }
|
DQL查询方式封装
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 <T> List<T> executeQuery(Class<T> clazz, String sql, Object... params) throws SQLException, InstantiationException, IllegalAccessException, NoSuchFieldException { Connection connection = JdbcUtilsV2.getConnection(); PreparedStatement preparedStatement = connection.prepareStatement(sql); if (params != null && params.length != 0) { for (int i = 1; i <= params.length; i++) { preparedStatement.setObject(i, params[i - 1]); } } ResultSet resultSet = preparedStatement.executeQuery(); List<T> list = new ArrayList<>(); ResultSetMetaData metaData = resultSet.getMetaData(); int columnCount = metaData.getColumnCount(); while (resultSet.next()) { T t = clazz.newInstance(); for (int i = 1; i <= columnCount; i++) { Object object = resultSet.getObject(i); String columnLabel = metaData.getColumnLabel(i);
Field field = clazz.getDeclaredField(columnLabel); field.setAccessible(true); field.set(t, object); } list.add(t);
} resultSet.close(); preparedStatement.close(); if (connection.getAutoCommit()) { JdbcUtilsV2.closeConnection(); } return list;
}
|