JDBC详解学习笔记
JDBC简介
架构时——没有什么是加一层解决不了的,如果有,就再加一层。
如tomcat集群上面的Nginx,Nginx集群上面的LVS.
JDBC是数据库驱动的接口规范,是SUN公司未来简化开发人员对数据库的统一操作而提供的一个规范。即Java数据库连接,(Java Database Connectivity,简称JDBC)。
这些规范有具体的数据库厂商去实现,而开发人员只需要掌握JDBC接口操作即可。
没有JDBC时:
通过JDBC时:
JDBC操作步骤——贾琏欲执事
口诀:贾琏欲执事——加连预执释
- 加载驱动
- 连接数据库
- 创建预编译语句
- 执行sql
- 释放资源
url键值对 | 描述 |
---|---|
useUnicode=true | 支持中文编码 |
characterEncoding=utf8 | 设置字符集编码为utf8 |
useSSL=true | 使用安全连接 |
JDBC对象解析
oracle没有数据库概念,一个模式就是一个库
statement对象
JDBC工具类示例
public class JDBCTest {
public static void main(String[] args) {
//查询、插入展示
select();
insert();
}
public static void select() {
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try {
conn = JDBCUtil.getConnection();
st = conn.createStatement();
String sql = "select * from user where 1=1";
rs = st.executeQuery(sql);
while (rs.next()) {
//参数为字段名
System.out.println(rs.getString("name"));
}
} catch (Exception throwables) {
throwables.printStackTrace();
} finally {
JDBCUtil.release(rs, st, conn);
}
}
public static void insert() {
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try {
conn = JDBCUtil.getConnection();
st = conn.createStatement();
String sql = "insert into user(`name`,age)values ('安安',1)";
int i = st.executeUpdate(sql);
if (i > 0) {
System.out.println("插入成功");
}
} catch (Exception throwables) {
throwables.printStackTrace();
} finally {
JDBCUtil.release(rs, st, conn);
}
}
}
//JDBC工具类,返回的Connection相当于一个数据库操作对象,可以用于事务等处理
public class JDBCUtil {
private static String URL = null;
private static String USERNAME = null;
private static String PASSWORD = null;
private static String DIRVIER = null;
static {
//通过classloader去获取配置文件的输入流
InputStream is = JDBCUtil.class.getClassLoader().getResourceAsStream("db.properties");
//再创建properties对象去load输入流
Properties properties = new Properties();
try {
properties.load(is);
} catch (IOException e) {
e.printStackTrace();
}
URL = properties.getProperty("roy.mysql.url");
USERNAME = properties.getProperty("roy.mysql.username");
PASSWORD = properties.getProperty("roy.mysql.password");
DIRVIER = properties.getProperty("roy.mysql.driver");
try {
Class.forName(DIRVIER);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(URL, USERNAME, PASSWORD);
}
public static void release(ResultSet rs, Statement st, Connection conn) {
try {
if (rs != null) {
rs.close();
}
if (st != null) {
st.close();
}
if (conn != null) {
conn.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
roy.mysql.url=jdbc:mysql://localhost:3306/mybatis_plus?useSSL=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
roy.mysql.username=root
roy.mysql.password=123456
roy.mysql.driver=com.mysql.cj.jdbc.Driver
#注意properties文件就是String类型的键值对,因此不需要再加""双引号
sql注入
所谓SQL注入,就是通过把SQL命令插入到Web表单递交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。
我们永远不要信任用户的输入,我们必须认定用户输入的数据都是不安全的,我们都需要对用户输入的数据进行过滤处理。
PreparedStatement——防止sql注入
PreparedStatement可以防止sql注入,其防止sql注入的本质是PreparedStatement会把传进来的参数当做字符,假如其中存在转义符如''
引号这种,会被直接转义处理;且因为预编译所以效率更高。
DB中执行的SQL为
select file from file where name = '\'test\' or 1=1'
把整个参数用引号包起来,并把参数中的引号作为转义字符,从而避免了参数也作为条件的一部分
示例
public class PreparedStatementTest {
public static void main(String[] args) {
select();
insert("白衣阿风",28);
}
public static void select() {
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try {
conn = JDBCUtil.getConnection();
String sql = "select * from user where 1=1 and `name` =?";
st = conn.prepareStatement(sql);
st.setString(1,"艾米");
rs = st.executeQuery();
while (rs.next()) {
//参数为字段名
System.out.println(rs.getString("email"));
}
} catch (Exception throwables) {
throwables.printStackTrace();
} finally {
JDBCUtil.release(rs, st, conn);
}
}
public static void insert(String name,Integer age) {
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try {
conn = JDBCUtil.getConnection();
String sql = "insert into user(`name`,age)values (?,?)";
st = conn.prepareStatement(sql);
st.setString(1,name);
st.setInt(2,age);
int i = st.executeUpdate();
if (i > 0) {
System.out.println("插入成功");
}
} catch (Exception throwables) {
throwables.printStackTrace();
} finally {
JDBCUtil.release(rs, st, conn);
}
}
}
Idea连接数据库
JDBC事务
代码示例
public class TranscationTest {
public static void main(String[] args) {
transcation("艾米哈珀","哈米国王",new BigDecimal("1500000"));
}
public static void transcation(String seller,String buyer, BigDecimal money) {
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try {
conn = JDBCUtil.getConnection();
//关闭事务自动提交,这时候表示自动开启事务
conn.setAutoCommit(false);
String sql1 = "update t_bank_account set money=money+? where `name`=?";
st = conn.prepareStatement(sql1);
st.setBigDecimal(1,money);
st.setString(2,seller);
int i = st.executeUpdate();
//测试事务异常
// System.out.println(1/0);
String sql2 = "update t_bank_account set money=money -? where `name`=?";
st = conn.prepareStatement(sql2);
st.setBigDecimal(1,money);
st.setString(2,buyer);
int j = st.executeUpdate();
if (j > 0 && i > 0) {
System.out.println("交易成功");
}
conn.commit();
} catch (Exception throwables) {
try {
conn.rollback();
} catch (SQLException e) {
e.printStackTrace();
}
throwables.printStackTrace();
} finally {
JDBCUtil.release(rs, st, conn);
}
}
}
数据库连接池——池化技术
池化技术的出现在于每次创建连接和释放连接都非常的耗费资源,现实场景中,我们更好的处理方法是通过池化技术来优化和提高这种每次都要创建释放资源的事情,就像我们的工厂生产产品或者银行对外提供服务,工厂不能有产品要生产才招一个临时工,生产完就解雇掉;银行不能每次要处理业务就开个门或者说开个窗口,处理好了就把处理窗口关闭了,两者在现实中我们都能观察到:工厂是有一定数量的稳定工人的,而银行的对外业务窗口也是稳定的,这就是池化技术。
数据库连接池的本质是实现一个DataSource接口。
开源数据源实现
- DBCP
- C3P0
- Druid(德鲁伊)阿里巴巴开源
使用了这些数据库连接池后,我们在项目开发中就不需要编写连接数据库的代码了。
DBCP及C3P0示例
maven依赖
<!-- dbcp连接池依赖,commons-dbcp内部已经依赖了commons-pool-->
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>commons-pool</groupId>
<artifactId>commons-pool</artifactId>
<version>1.5.4</version>
</dependency>
<!-- c3p0连接依赖,c3p0内部已经依赖了mchange-commons-java-->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
dbcp配置文件——dbcp.properties
driverClassName=com.mysql.cj.jdbc.Driver
#url
url=jdbc:mysql://localhost:3306/mybatis_plus?useSSL=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
#用户名
username=root
#密码
password=123456
#初试连接数
initialSize=30
#最大活跃数
maxTotal=30
#最大idle数
maxIdle=10
#最小idle数
minIdle=5
#最长等待时间(毫秒)
maxWaitMillis=1000
#程序中的连接不使用后是否被连接池回收(该版本要使用removeAbandonedOnMaintenance和removeAbandonedOnBorrow)
#removeAbandoned=true
removeAbandonedOnMaintenance=true
removeAbandonedOnBorrow=true
#连接在所指定的秒数内未使用才会被删除(秒)(为配合测试程序才配置为1秒)
removeAbandonedTimeout=1
c3p0配置文件——c3p0-config.xml
<c3p0-config>
<!-- 使用默认的配置读取连接池对象 -->
<default-config>
<!-- 连接参数 -->
<property name="driverClass">com.mysql.cj.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/mybatis_plus?useSSL=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8</property>
<property name="user">root</property>
<property name="password">123456</property>
<!-- 连接池参数 -->
<!--初始化申请的连接数量-->
<property name="initialPoolSize">5</property>
<!--最大的连接数量-->
<property name="maxPoolSize">10</property>
<!--超时时间-->
<property name="checkoutTimeout">3000</property>
</default-config>
<!-- 其他如oracle、mysql等的配置,要使用时只需要指定要加载的配置名问参数,不传则使用默认配置-->
<named-config name="mysql">
<!-- 连接参数 -->
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/test</property>
<property name="user">root</property>
<property name="password">root</property>
<!-- 连接池参数 -->
<property name="initialPoolSize">5</property>
<property name="maxPoolSize">8</property>
<property name="checkoutTimeout">1000</property>
</named-config>
</c3p0-config>
public class C3P0Util {
private static DataSource dataSource = null;
static {
//通过classloader去获取配置文件的输入流
try {
dataSource = new ComboPooledDataSource();
//不指定配置名称则读取默认的c3p0配置
//xml文件不需要读取成流便能读取文件
// dataSource = new ComboPooledDataSource("mysql");
} catch (Exception e) {
e.printStackTrace();
}
}
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
public static void release(ResultSet rs, Statement st, Connection conn) {
try {
if (rs != null) {
rs.close();
}
if (st != null) {
st.close();
}
if (conn != null) {
conn.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class DBCPUtil {
private static DataSource dataSource = null;
static {
//通过classloader去获取配置文件的输入流
InputStream is = DBCPUtil.class.getClassLoader().getResourceAsStream("dbcp.properties");
//再创建properties对象去load输入流
Properties properties = new Properties();
//Properties文件注意要加载流,否则读不到文件会报错如下
//org.apache.commons.dbcp.SQLNestedException: Cannot create JDBC driver of class '' for connect URL 'null'
try {
properties.load(is);
} catch (IOException e) {
e.printStackTrace();
}
try {
dataSource = BasicDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
}
}
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
public static void release(ResultSet rs, Statement st, Connection conn) {
try {
if (rs != null) {
rs.close();
}
if (st != null) {
st.close();
}
if (conn != null) {
conn.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class ConnectionPoolTest {
public static void main(String[] args) {
c3p0();
dbcp();
}
public static void c3p0() {
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try {
conn = C3P0Util.getConnection();
st = conn.createStatement();
String sql = "select * from user where 1=1";
rs = st.executeQuery(sql);
while (rs.next()) {
//参数为字段名
System.out.println(rs.getString("name"));
}
} catch (Exception throwables) {
throwables.printStackTrace();
} finally {
JDBCUtil.release(rs, st, conn);
}
}
public static void dbcp() {
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try {
conn = DBCPUtil.getConnection();
st = conn.createStatement();
String sql = "insert into user(`name`,age)values ('安安啊',1)";
int i = st.executeUpdate(sql);
if (i > 0) {
System.out.println("插入成功");
}
} catch (Exception throwables) {
throwables.printStackTrace();
} finally {
JDBCUtil.release(rs, st, conn);
}
}
}
参考文献
[狂神MySQL基础](