JDBC
JDBC
数据库驱动
驱动:声卡,显卡,数据库
我们的程序会通过 数据库 驱动,和数据库打交道!
JDBC
SUN 公司为了简化 开发人员的(对数据库的统一)操作,提供了一个(java操作数据库的)规范,俗称 JDBC
这些规范的实现由具体的厂商去做~
对于开发人员来说,我们只需要掌握JDBC接口的操作即可!
还需要导入一个数据库驱动包 mysql-connector-java-5.1.47.jar
第一个JDBC程序
-
创建测试数据库
-
导入数据库驱动
把数据库驱动包放到项目lib包中,并且添加到项目库中(Add as Library)
-
编写测试代码
//我的第一个JDBC程序 public class jdbcFirstDemo { public static void main(String[] args) throws Exception { //1.加载驱动 Class.forName("com.mysql.jdbc.Driver");//固定写法,加载驱动 //2.用户信息和url //ssmlibrary?useUnicode=true&characterEncoding=utf8&useSSL=true //数据库名?使用Unicode编码,避免中文乱码 & 使用utf8 & 使用安全连接 //新版本后面还要加时区 String url = "jdbc:mysql://localhost:3306/ssmlibrary?useUnicode=true&characterEncoding=utf8&useSSL=true"; String username = "root"; String password = "root"; //3.连接成功,数据库对象 Connection 代表数据库 Connection connection = DriverManager.getConnection(url, username, password); //4.执行SQL的对象 Statement 执行sql的对象 Statement statement = connection.createStatement(); //5.执行SQL的对象去执行SQL,可能存在结果,查看返回 String sql = "SELECT * FROM users"; //返回的结果集,结果集中封装了我们全部的查询出来的结果 ResultSet resultSet = statement.executeQuery(sql); while (resultSet.next()) { System.out.print("userID = " + resultSet.getObject("userID")); System.out.print(" userPassword = " + resultSet.getObject("userPassword")); System.out.println(" userPermission = " + resultSet.getObject("userPermission")); } //6.释放连接 resultSet.close(); statement.close(); connection.close(); } }
步骤总结:
- 加载驱动
- 连接数据库 DriverManager
- 获得执行sql的对象 Statement
- 获得返回的结果集
- 释放连接
JDBC中对象解释
DriverManager
DriverManager.registerDriver(new Driver());
Class.forName("com.mysql.jdbc.Driver");
建议使用第二种,因为第一种从源代码来看其实代码是静态代码块,使用第一种比第二种多注册一次,比较多余
URL
String url = "jdbc:mysql://localhost:3306/ssmlibrary?useUnicode=true&characterEncoding=utf8&useSSL=true";
//mysql默认端口号3306
//jdbc:mysql://主机地址:端口号/数据库名?参数1&参数2&参数3
//oralce默认端口号1521
//jdbc:oracle:thin:@localhost:1521:sid
Connection
Connection connection = DriverManager.getConnection(url, username, password);
//connection 代表数据库
//数据库设置自动提交
//事务提交
// 事务滚回
connection.setAutoCommit();
connection.commit();
connection.rollback();
Statement执行SQL的对象 PrepareStatement执行的SQL对象
String sql = "SELECT * FROM users"; //编写SQL
statement.executeQuery();//查询操作返回 Resultset
statement.execute();//执行任何SQL
statement.executeUpdate();//更新、插入、删除。都是用这个,返回一个受影响的行数
ResultSet查询的结果集:封装了所有的查询结果
获得指定的数据类型
resultSet.getObject();//在不知道列类型的情况下使用
//如果知道列的类型就使用指定的类型
resultSet.getInt();
resultSet.getString();
resultSet.getFloat();
resultSet.getDate();
……
遍历,指针
resultSet.beforeFirst();//移动到最前面
resultSet.afterLast();//移动到最后面
resultSet.next();//移动到下一个数据
resultSet.previous();//移动到前一行
resultSet.absolute();//移动到指定行
释放资源
resultSet.close();
statement.close();
connection.close();//耗资源,用完关掉
statement对象
Jdbc中的statement对象用于向数据库发送SQL语句,想完成对数据库的增删改査,只需要通过这个对象向数据库发送增删改查语句即可。
Statement对象的executeUpdate方法,用于向数据库发送增、删、改的sql语句,executeUpdate执行完后,将会返回一个整数(即增删改语句导致了数据库几行数据发生了变化)。
Statement.executeQuerv方法用于向数据库发送査询语句,executeQuerv方法返回代表査询结果的Resultset对象
下面通过分装工具类解耦来实现数据库增删改查操作CURD
目录结构
资源文件db.properties
driver = com.mysql.jdbc.Driver
url = jdbc:mysql://localhost:3306/ssmlibrary?useUnicode=true&characterEncoding=utf8&useSSL=true
username = root
password = root
工具类 JdbcUtils
public class JdbcUtils {
private static String dirver = null;
private static String url = null;
private static String username = null;
private static String password = null;
static {
try {
//用到了反射
//获得资源流,name就是文件名,放在src目录下,否则要添加报名
InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties");
Properties properties = new Properties();
//转换成Properties对象
properties.load(in);
//可以提取资源了
dirver = properties.getProperty("driver");
url = properties.getProperty("url");
username = properties.getProperty("username");
password = properties.getProperty("password");
//1.驱动只用加载一次
Class.forName(dirver);
} 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) {
throw new RuntimeException(e);
}
}
if (st != null) {
try {
st.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
}
增加
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();//获得SQL的执行对象
String sql = "INSERT INTO users(userID,userPassword,userPermission)" +
"VALUES (1212,1212,1);";
int i = st.executeUpdate(sql);
if (i > 0) {
System.out.println("插入成功!");
}
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
JdbcUtils.release(conn, st, rs);
}
}
}
删除
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();//获得SQL的执行对象
//这里users后面要和where分隔开,否则出错
String sql = "DELETE FROM users " +
"WHERE userID = 1212;";
int i = st.executeUpdate(sql);
if (i > 0) {
System.out.println("删除成功!");
}
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
JdbcUtils.release(conn, st, rs);
}
}
}
更新
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();//获得SQL的执行对象
String sql = "UPDATE users " +
"SET userPassword = 0, userPermission = 0 " +
"WHERE userID = 1212;";
int i = st.executeUpdate(sql);
if (i > 0) {
System.out.println("更新成功!");
}
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
JdbcUtils.release(conn, st, rs);
}
}
}
查询
public class TestSelect {
public static void main(String[] args) {
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();//获取数据库连接
st = conn.createStatement();//获得SQL的执行对象
//这里users后面要和where分隔开,否则出错
String sql = "SELECT * " +
"FROM users " +
"WHERE userID = 1212;";
rs = st.executeQuery(sql);
while (rs.next()) {
System.out.print(rs.getInt("userID") + " ");
System.out.print(rs.getInt("userPassword") + " ");
System.out.println(rs.getInt("userPermission"));
}
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
JdbcUtils.release(conn, st, rs);
}
}
}
SQL注入问题
sql存在漏洞,会被攻击导致数据泄露,SQL会被拼接 or,以后会使用PrepareStatement对象来避免
public class TestSQL {
public static void main(String[] args) {
// TestSQL.login(1212 + "", 0 + "");
//输入非法字符串,但依然可以通过,本质上用了1=1
TestSQL.login("''or '1 = 1'", "''or '1 = 1'");
}
public static void login(String id, String password) {
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();//获取数据库连接
st = conn.createStatement();//获得SQL的执行对象
//这里users后面要和where分隔开,否则出错
String sql = "SELECT * " +
"FROM users " +
"WHERE userID = " + id + " and userPassword = "+ password + ";";
rs = st.executeQuery(sql);
while (rs.next()) {
System.out.print(rs.getInt("userID") + " ");
System.out.print(rs.getInt("userPassword") + " ");
System.out.println(rs.getInt("userPermission"));
}
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
JdbcUtils.release(conn, st, rs);
}
}
}
PreparedStatement对象
Preparedstatement可以防止SQL注入。效率更好!
本质上就是在Statement上加了一层,做了一些工作
public interface PreparedStatement extends Statement
增加
public class TestPrepared {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();//获取数据库连接
//区别
//使用? 占位符代替参数
String sql = "INSERT INTO users(userID,userPassword,userPermission)" +
"VALUES (?,?,?);";
st = conn.prepareStatement(sql);//预编译SQL,先写sql,然后不执行
//手动给参数赋值
st.setInt(1, 1212);
st.setInt(2, 1212);
st.setInt(3, 1212);
//执行
int i = st.executeUpdate();
if (i > 0) {
System.out.println("插入成功!");
}
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
JdbcUtils.release(conn, st, rs);
}
}
}
删除,一下只展现关键代码
//区别
//使用? 占位符代替参数
String sql = "DELETE FROM users " +
"WHERE userID = ?;";
st = conn.prepareStatement(sql);//预编译SQL,先写sql,然后不执行
//手动给参数赋值
st.setInt(1, 1212);
//执行
int i = st.executeUpdate();
更新
//区别
//使用? 占位符代替参数
String sql = "UPDATE users SET userPassword = ? WHERE userID = ?;";
st = conn.prepareStatement(sql);//预编译SQL,先写sql,然后不执行
//手动给参数赋值
st.setInt(1, 999);
st.setInt(2, 1212);
//执行
int i = st.executeUpdate();
查询
String sql = "SELECT * FROM users WHERE userID = ?;";
st = conn.prepareStatement(sql);//预编译SQL,先写sql,然后不执行
//手动给参数赋值
st.setInt(1, 1212);
//执行
rs = st.executeQuery();
登录功能,避免SQL注入问题
//PreparedStatement 防止SQL注入的本质,把传递进来的参数当微字符
//假设其中存在转义字符,比如说'会被直接转义
String sql = "SELECT * FROM users WHERE userID = ? and userPassword = ?;";
st = conn.createStatement(sql);//获得SQL的执行对象
st.setInt(1, id);
st.setInt(2, password);
rs = st.executeQuery();
使用IDEA连接数据库
打开数据库面板
连接数据库
加载数据库
可以直接在表中修改,最后记得提交修改,否则没用
打开sql语句输入面板
事务
要么都成功,要么都失败
ACID原则
原子性:要么全部完成,要么都不完成
一致性:总数不变
隔离性:多个进程互不干扰
持久性:一旦提交不可逆,持久化到数据库了
隔离性的问题:
脏读:一个事务读取了另一个没有提交的事务
不可重复读:在同一个事务内,重复读取表中的数据,表数据发生了改变
虚读(幻读):在一个事务内,读取到了别人插入的数据,导致前后读出来结果不一致
代码实现
- 开启事务
conn.setAutoCommit(false);
- 一组业务执行完毕,提交事务
- 可以在catch 语句中显示的定义 回滚语句,但默认失败就会回滚
public class TestTransaction {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();//获取数据库连接
//关闭数据库的自动提交,自动会开启事务
conn.setAutoCommit(false);//开启事务
String sql1 = "UPDATE users SET userPassword = userPassword+1 WHERE userID = 1212;";
st = conn.prepareStatement(sql1);//预编译SQL,先写sql,然后不执行
st.executeUpdate();
//报错
// int i = 1 / 0;
String sql2 = "UPDATE users SET userPassword = userPassword-2 WHERE userID = 1212;";
st = conn.prepareStatement(sql2);//预编译SQL,先写sql,然后不执行
st.executeUpdate();
//业务完毕,提交事务
conn.commit();
System.out.println("成功!");
} catch (SQLException e) {
// try {
// //如果失败,则回滚事务,其实也不用写,默认自动回滚事务
// conn.rollback();
// } catch (SQLException ex) {
// throw new RuntimeException(ex);
// }
throw new RuntimeException(e);
} finally {
JdbcUtils.release(conn, st, rs);
}
}
}
数据库连接池
数据库连接——执行完毕——释放
连接——释放十分浪费系统资源
池化技术:准备一些预先的资源,过来就连接预先准备好的,避免浪费
相关设置
最小连接数:10(常和常用连接数一直)
最大连接数:15(业务最高承载上限)
等待超时:100ms(请求排队等待,超时了就不要等了)
编写连接池,实现一个接口 DataSource
开源数据源实现
DBCP
C3P0
Druid:阿里巴巴
使用了这些数据库连接池之后,我们在项目开发中就不需要编写连接数据库的代码了!
DBCP
需要用到的 jar 包
commons-dbcp-1.4、commons-pool-1.6
C3P0
需要用到的jar 包
c3p0-0.9.5.5、mchange-commons-java-0.2.19
结论
无论使用什么数据源,本质还是一样的,DataSource接口不会变,方法就不会变
本文作者:韩熙隐ario
本文链接:https://www.cnblogs.com/arioya/p/18697895
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步