由于不同的数据库有不同的驱动,于是就有SUN公司为了简化开发人员,就对数据库统一提供了java操作数据库一个规范(JDBC)。这些规范的实现由具体的厂商去做!对于开发人员来说只需要掌握jdbc的接口操作即可。我们只需要导入对应的数据库的驱动包即可。
// 创建连接的工具类
package com.dbh.student02.utils;
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 pwd=null;
properties
static{
try{
// 使用db.properties
//JDBCUtil.class.getClassLoader().getResourceAsStream("jdbc.properties")是返回一个读取指定资源的输入流
InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties");
//创建一个空的properties
Properties properties=new Properties();
//pro.load(in)是从输入流中读取属性列表
properties.load(in);
// 从properties读取内容并给他赋值
driver= properties.getProperty("driver");
url= properties.getProperty("url");
username=properties.getProperty("username");
pwd=properties.getProperty("password");
// 加载驱动,只需要加载一次
Class.forName(driver);
}catch (Exception e){
e.printStackTrace();
}
}
// 获得连接
// connection 代表数据库 connection可以开启事务,可以回滚事务可以设置是否自动提交
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url, username, pwd);
}
// 关闭连接
// Statement 执行sql的对象 PreparedStatement也是sql的对象但是相对于statement更加安全
// 查询statement.executeQuery 返回值ResultSet
// 删除,修改,增加多用一个,只是具体的sql statement.executeUpdate() 返回值是受影响的行数
// statement.execute执行全部
//ResultSet可以获得指定字段的值 resultSet.getInt(),resultSet.getString(), resultSet.getObject(), resultSet.getFloat(),resultSet.getDate(), resultSet.getDouble()...
public static void release(Connection conn, ResultSet rs, Statement st){
if(conn!=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(rs!=null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(st!=null){
try {
st.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
// 增加
package com.dbh.student02.demo;
import com.dbh.student02.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class TestInster {
public static void main(String[] args) {
Connection conn=null;
ResultSet rs=null;
Statement st=null;
try{
conn= JdbcUtils.getConnection();
st=conn.createStatement();
String sql="insert into user(`username`,`pwd`,`sex`,`age`) VALUES('ybt','123','男',12)";
int i = st.executeUpdate(sql);
if(i>0){
System.out.println("插入成功");
}else{
System.out.println("插入失败");
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.release(conn,rs,st);
}
}
}
// 删除
package com.dbh.student02.demo;
import com.dbh.student02.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class TestDelete {
public static void main(String[] args) {
Connection conn=null;
Statement st=null;
ResultSet rs=null;
try {
conn=JdbcUtils.getConnection();
st=conn.createStatement();
String sql="DELETE FROM `user` WHERE id=1";
int i=st.executeUpdate(sql);
if(i>0){
System.out.println("删除成功");
}else{
System.out.println("删除失败");
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.release(conn,rs,st);
}
}
}
//修改
package com.dbh.student02.demo;
import com.dbh.student02.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class TestUpdate {
public static void main(String[] args) {
Connection conn=null;
Statement st=null;
ResultSet rs=null;
try {
conn= JdbcUtils.getConnection();
st=conn.createStatement();
String sql="UPDATE `user` SET pwd=789 WHERE id=2";
int i=st.executeUpdate(sql);
if(i>0){
System.out.println("修改成功");
}else{
System.out.println("修改失败");
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.release(conn,rs,st);
}
}
}
// 查看
package com.dbh.student02.demo;
import com.dbh.student02.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class TestSelect {
public static void main(String[] args) {
Connection conn=null;
ResultSet rs=null;
Statement st=null;
try {
conn= JdbcUtils.getConnection();
st=conn.createStatement();
String sql="select *from `user`";
rs = st.executeQuery(sql);
while (rs.next()) {
System.out.println(rs.getString("username"));
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.release(conn,rs,st);
}
}
}
SQL注入的问题
sql存在漏洞,会被攻击,导致数据泄露。
select *from user
where username
=’‘or ’ 1=1’ and pwd =123456,**sql存在拼接 **
PreparedStatement
可以防止SQL注入,效果更好
本质就是把传递过来的参数当作字符。
package com.dbh.student02.demo;
import com.dbh.student02.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class SQLPreparedStatement {
public static void main(String[] args) {
Connection conn=null;
PreparedStatement ps=null;
ResultSet rs=null;
try {
conn= JdbcUtils.getConnection();
//可以使用?占位符代替参数
String sql="insert into `user`(`username`,`pwd`,`sex`,`age`) values(?,?,?,?)";
ps= conn.prepareStatement(sql);//预编译SQL,先写SQL,然后不执行
ps.setString(1,"dkz");
ps.setString(2,"123");
ps.setString(3,"男");
ps.setInt(4,100);
int i = ps.executeUpdate();
if(i>0){
System.out.println("插入成功");
}else{
System.out.println("插入失败");
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
IDEA连接mysql
JDBC操作事务
要么成功,要么失败
ACID原则
原子性:要么全部完成,要么全部不完成
一致性:总数不变
持久性:多个进程互不干扰
隔离性:一旦提交就不可逆,持久化到数据库了
隔离性的问题:
脏读:一个事务读取了另一个没有提交的事务
不可重复读: 在同意个事务内,重复读取表中的数据,表的数据发生了改变
幻读/虚读:在一个事务内,读取到了,别人插入的数据。导致前后读出来的结果不一致
事务的个隔离级别(mysql默认可重复读):
读未提交,读以提交,可重复读,可串行
模拟转账
package com.dbh.student02.demo;
import com.dbh.student02.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class TestTransaction {
public static void main(String[] args) {
Connection conn=null;
ResultSet rs=null;
PreparedStatement ps=null;
try {
conn= JdbcUtils.getConnection();
//关闭数据库的自动提交,自动会开启事务
conn.setAutoCommit(false);
//执行转账业务
String sql="update `account` set `money`=money-100 where name= 'A'";
ps=conn.prepareStatement(sql);
ps.executeUpdate();
// 模仿失败,如果没加事务,理论上面的sql应该是成功的。但是并没有,这样更好的证明了事务之间是不可分割的,要么失败,要么成功.
int i=1/0;
String sql2="update `account` set `money`=money+100 where name= ''";
ps=conn.prepareStatement(sql2);
ps.executeUpdate();
//业务执行完毕需要提交事务
conn.commit();
System.out.println("操作成功");
//提交完成事务后需要重新启动
conn.setAutoCommit(true);
} catch (SQLException e) {
try {
System.out.println("操作失败,需要回滚");
conn.rollback();//如果失败就回滚事务,就算没写,也会自动回滚,只要是在执行的事务额过程中报了错误。
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
}finally {
JdbcUtils.release(conn,rs,ps);
}
}
}
数据库连接池
数据库连接—》执行完毕—》释放
连接-- 释放 十分浪费系统资源
池化技术:准备一些预先的资源,过来就连接预先准备好的
更据你常用的连接数,来设置最小的连接数
最小连接数:10
最大连接数: 100 业务的承载上限
如果是超过了最大链接数就需要排队等待
等待超时:100ms
编写连接池,只需要实现一个接口 DataSource
开源数据源实现
DBCP
C3P0
Druid: 阿里巴巴的
使用这些数据库连接池后,我们在项目开发中,就不需要编写连接数据库的代码
DBCP
需要用的jar包(以及数据库的驱动)
# 连接设置 这里的名字,是DBCP数据源定义好的
dirverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/dbhblog?userUnicode=true&characterEncoding=utf8&useSSL=true
username=root
password=123456
#<!-- 初始化连接 -->
initialSize=10
#最大连接数量
maxActive=50
#<!-- 最大空闲连接 -->
maxIdle=20
#<!-- 最小空闲连接 -->
minIdle=5
#JDBC驱动建立连接时附带的连接属性属性的格式必须为这样:【属性名=property;//】
#<!-- 超时等待时间以毫秒为单位 6000毫秒/1000等于60秒 -->
maxWait=60000
#注意:"user" 与 "password" 两个属性会被明确地传递,因此这里不需要包含他们。
connectionProperties=useUnicode=true;characterEncoding=utf8
#指定由连接池所创建的连接的自动提交(auto-commit)状态。
defaultAutoCommit=true
#driver default 指定由连接池所创建的连接的只读(read-only)状态。
#如果没有设置该值,则“setReadOnly”方法将不被调用。(某些驱动并不支持只读模式,如:Informix)
defaultReadOnly=
#driver default 指定由连接池所创建的连接的事务级别(TransactionIsolation)。
#可用值为下列之一:(详情可见javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE
defaultTransactionIsolation=READ_UNCOMMITTED
// 使用DBCP写的util类
package com.dbh.student02.demo;
import org.apache.commons.dbcp.BasicDataSource;
import org.apache.commons.dbcp.BasicDataSourceFactory;
import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
public class TestDBCP {
private static DataSource dataSource=null;
static{
try {
InputStream in = TestDBCP.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, ResultSet rs, Statement st){
if(conn!=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(rs!=null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(st!=null){
try {
st.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
// 使用帮助类得到具体类
package com.dbh.student02.demo;
import com.dbh.student02.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class TestSelect {
public static void main(String[] args) {
Connection conn=null;
ResultSet rs=null;
Statement st=null;
try {
conn= TestDBCP.getConnection();
st=conn.createStatement();
String sql="select *from `user`";
rs = st.executeQuery(sql);
while (rs.next()) {
System.out.println(rs.getString("username"));
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
TestDBCP.release(conn,rs,st);
}
}
}
C3P0
需要的jar包
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<!-- 默认配置,如果没有指定则使用这个配置 -->
<default-config>
<property name="user">root</property>
<property name="password">123456</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/zhanghanlun?userUnicode=true&characterEncoding=utf8&useSSL=true</property>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="checkoutTimeout">30000</property>
<property name="idleConnectionTestPeriod">30</property>
<property name="initialPoolSize">3</property>
<property name="maxIdleTime">30</property>
<property name="maxPoolSize">100</property>
<property name="minPoolSize">2</property>
<property name="maxStatements">200</property>
</default-config>
<!-- 命名的配置,可以通过方法调用实现 -->
<named-config name="MYSQL">
<property name="user">root</property>
<property name="password">123456</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/dbhblog?userUnicode=true&characterEncoding=utf8&useSSL=true</property>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<!-- 如果池中数据连接不够时一次增长多少个 -->
<property name="acquireIncrement">5</property>
<!-- 初始化数据库连接池时连接的数量 -->
<property name="initialPoolSize">20</property>
<!-- 数据库连接池中的最大的数据库连接数 -->
<property name="maxPoolSize">25</property>
<!-- 数据库连接池中的最小的数据库连接数 -->
<property name="minPoolSize">5</property>
</named-config>
</c3p0-config>
package com.dbh.student02.demo;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class TestC3P0 {
private static ComboPooledDataSource dataSource=null;
static {
try{
// 代码配置
/* dataSource=new ComboPooledDataSource("MYSQL");
dataSource.setDriverClass();
dataSource.setUser();
dataSource.setPassword();
dataSource.setJdbcUrl();
dataSource.setMaxPoolSize();*/
dataSource=new ComboPooledDataSource("MYSQL");//配置文件写法,一般是使用这一种
}catch (Exception e){
e.printStackTrace();
}
}
// 获取连接
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();//从数据源获取连接
}
// 关闭连接
public static void release(Connection conn, ResultSet rs, Statement st){
if(conn!=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(rs!=null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(st!=null){
try {
st.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
// 实现
package com.dbh.student02.demo;
import com.dbh.student02.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class TestSelect {
public static void main(String[] args) {
Connection conn=null;
ResultSet rs=null;
Statement st=null;
try {
conn= TestC3P0.getConnection();
st=conn.createStatement();
String sql="select *from `user`";
rs = st.executeQuery(sql);
while (rs.next()) {
System.out.println(rs.getString("username"));
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
TestDBCP.release(conn,rs,st);
}
}
}
无论是使用什么数据源 ,本质还是一样,DataSource的接口是不会变的。方法就不会变(获取连接)。