DML
Data Manipulation Language : 数据操作语言
涉及的关键字有 : delete,update,insert
* 1 导包,创建lib,复制进去,Build Path
* 2 编码
*
* 2.1 加载驱动(驱动注册)com.mysql.jdbc.Driver
*
* 2.2 连接数据库
*
* 2.3 执行SQL
*
* 2.4 接收执行结果
*
* 2.5 获取数据
*
* 2.6 关闭资源
* 如果乱码 可以通过url后面 添加东西解决
*
* jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf8
*
package mysql;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
public class DML {
public static void main(String[] args) {
add();
}
public static void add(){
Connection conn =null;
Statement stmt=null;
try{
//加载驱动
Class.forName("com.mysql.jdbc.Driver");
//连接数据库,jdbc:mysql://127.0.0.1:3306/数据库名","用户名","密码"
conn=DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test","t","1111");
//创建语句传输对象
stmt=conn.createStatement();
String sql =
"insert into student (id,username,password) values (3,'张三','root'), (4,'张三','root'), (5,'张三','root')";
int count=stmt.executeUpdate(sql);
System.out.println("影响了"+count+"条");
}catch(Exception e){
e.printStackTrace();
}finally{
try{
比如查询语句写错了等,可能会导致释放资源出现问题,这时候会抛出异常,那么关闭语句就不会执行,所以我们应该使用try...catch...finally来优化一下
if(stmt!=null){
stmt.close();
}if(conn!=null){
conn.close();
}
}catch(Exception e){
e.printStackTrace();
}
}
}
}
PreparedStatement
添加或者更新的时候,尽量使用 PreparedStatement ,而不是使用Statement。
Statement 和 PreparedStatement 的区别 :
Statement用于执行静态SQL语句,在执行的时候,必须指定一个事先准备好的SQL语句,并且相对不安全,会有SQL注入的风险
PreparedStatement是预编译的SQL语句对象,sql语句被预编译并保存在对象中, 被封装的sql语句中可以使用动态包含的参数 ? ,
在执行的时候,可以为?传递参数 使用PreparedStatement对象执行sql的时候,sql被数据库进行预编译和预解析,然后被放到缓冲区,
每当执行同一个PreparedStatement对象时,他就会被解析一次,但不会被再次编译 可以重复使用,可以减少编译次数,提高数据库性能
并且能够避免SQL注入,相对安全(把’ 单引号 使用 \ 转义,避免SQL注入 )
package mysql;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class JDBC_PreparedStatement {
public static void main(String[] args) {
--使用Statement时,如果传入的是"张三 or '1'=1","1223",此时都会因为1=1为真匹配成功
updatePreparedStatement("张三", "1223");
}
public static void updatePreparedStatement(String username, String password) {
Connection conn = null;
PreparedStatement stmt = null;
try {
// 1 加载驱动,驱动注册
Class.forName("com.mysql.jdbc.Driver");
// Class.forName("com.mysql.jdbc.Driver").newInstance();
// 2 连接数据库
// jdbc:mysql://IP:端口/数据库
conn = DriverManager.getConnection(
"jdbc:mysql://127.0.0.1:3306/test", "t", "1111");
// 更新,用 ? 代替参数,叫占位符,也叫通配符
String sql = "update student set password = ? where username = ?";
// 3 创建语句传输对象
stmt = conn.prepareStatement(sql);
// 设置第一个问号的值
stmt.setString(1, password);
stmt.setString(2, username);
// 返回int,操作了几条数据
int count = stmt.executeUpdate();
System.out.println("影响了 : " + count + " 条数据");
} catch (Exception e) {
e.printStackTrace();
} finally {
// 5 关闭资源
try {
if (stmt != null) {
stmt.close();
}
if (conn != null) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
硬代码优化
如上,我们项目开发完成测试通过后,通常是要打包发布给别人使用的,如果我们把一些配置信息写死到代码中(这种行为叫硬编码,hardcode),别人就无法修改了
比如别人使用的MySQL服务器对应的IP是10.0.0.1,端口号是9300,用户名是mysqluser、密码是123456。
那么我们只能改代码再重新打包发布,如果有多个用户,他们对应的配置信息都不同,那么我们要针对不同的用户打包发布多次。
以上显然是没必要的,因为我们开发的是程序,只要业务需求/逻辑不变,我们就无需多次打包发布。对此我们的解决方案是,尽量避免硬编码, 将数据与程序分离解耦,将配置信息存储到配置文件中,程序运行时读取配置文件,不同用户只需按自己的实际情况修改配置文件即可。
1.创建jdbc.properties配置文件
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/test
username=t
password=1111
1.1加载文件
package util;
import java.io.IOException;
import java.util.Properties;
public class PropertiesUtil {
private static Properties properties = null;
private PropertiesUtil() {
}
public static Properties getProperties() {
return getProperties("jdbc.properties");
}
public static Properties getProperties(String fileName) {
if (properties == null) {
properties = new Properties();
try {
// PropertiesUtil.class : 获取运行时类(正在运行的类)
// getClassLoader() : 获取类加载器
// getResourceAsStream : 把指定资源转换为流对象
// load : 传入流对象,把对象中数据转换为map存储,间隔符为 = 或 :
properties.load(PropertiesUtil.class.getClassLoader()
.getResourceAsStream(fileName));
} catch (IOException e) {
e.printStackTrace();
}
}
return properties;
}
}
2.DBUtil工具类
package util;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
public class DBUtil {
public static Connection getConnection() throws ClassNotFoundException,
SQLException {
Properties properties = PropertiesUtil.getProperties();
String username = properties.getProperty("username");
String password = properties.getProperty("password");
String driver = properties.getProperty("driver");
String url = properties.getProperty("url");
Class.forName(driver);
Connection conn = DriverManager.getConnection(url, username, password);
return conn;
}
// 因为resultSet , connection等 都继承了 AutoCloseable 所以 这里直接写父类即可,因为多态
public static void close(AutoCloseable obj) {
if (obj != null) {
try {
obj.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
3.测试
Batch多语句操作
在一次任务中,执行多条数据
PreparedStatement和Statement实现
public class JDBC_05_Batch {
public static void main(String[] args) {
long start = System.currentTimeMillis();
test_01();
long end = System.currentTimeMillis();
System.out.println(end-start);
}
public static void test_01() {
Connection connection = null;
PreparedStatement preparedStatement = null;
try {
connection = DBUtil.getConnection();
preparedStatement = connection
.prepareStatement("insert into t_user (id,username,password) values(?,?,?)");
preparedStatement.setInt(1, 6);
preparedStatement.setString(2, "李四");
preparedStatement.setString(3, "admin");
preparedStatement.addBatch();
preparedStatement.setInt(1, 7);
preparedStatement.setString(2, "李四");
preparedStatement.setString(3, "admin");
preparedStatement.addBatch();
preparedStatement.setInt(1, 8);
preparedStatement.setString(2, "李四");
preparedStatement.setString(3, "admin");
preparedStatement.addBatch();
// 一次性执行
preparedStatement.executeBatch();
} catch (Exception e) {
e.printStackTrace();
} finally {
DBUtil.close(preparedStatement);
DBUtil.close(connection);
}
}
}
package mysql;
import java.sql.Connection;
import java.sql.Statement;
import util.DBUtil;
/**
*比如在一次任务中,需要多次插入,就可以使用batch
*
*/
public class J_Batch {
public static void main(String[] args) {
test1();
}
public static void test1(){
Connection connection=null;
Statement statement=null;
try{
connection=DBUtil.getConnection();
statement=connection.createStatement();
statement.addBatch("insert into student (id,username,password) values(1,'王一','root')");
statement.addBatch("insert into student (id,username,password) values(2,'李二','rooqt')");
statement.executeBatch();
}catch(Exception e){
e.printStackTrace();
}finally{
DBUtil.close(statement);
DBUtil.close(connection);
}
}
}
事务机制
Transaction事务机制管理
默认情况下,是执行一条SQL语句就保存一次,那么比如我需要 有三条数据同时成功同时失败,这个时候就需要开启事务机制了
如果开启事务机制,执行中发生问题,会回滚到没有操作之前,相当于什么也没有发生过
package mysql;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/**
* 事务机制 , 保证多次操作同时成功同时失败
*
* 执行SQL之后,并不会保存到数据库中,而是保存到缓存中,当执行commit的时候,才会持久化到数据库中
*
* 而 默认 是 每条SQL执行完之后,自动commit,所以想要做到事务管理 必须把默认的自动提交关闭
*
*/
import util.DBUtil;
public class Transaction {
public static void main(String[] args) {
insert(1,"adf","af");
}
public static void insert(int id,String name,String pass){
Connection connection=null;
PreparedStatement prepareStatement=null;
try{
connection=DBUtil.getConnection();
//取消自动提交即开启事务
connection.setAutoCommit(false);
prepareStatement=connection.prepareStatement("insert into student "
+ "(id,username,password) values (?,?,?)");
prepareStatement.setInt(1,id);
prepareStatement.setString(2, name);
prepareStatement.setString(3, pass);
prepareStatement.addBatch();
//一次性执行,不开启会导致数据插入失败
prepareStatement.executeBatch();
//事务提交
connection.commit();
System.out.println("over");
}catch(Exception e){
e.printStackTrace();
if(connection!=null){
try {
// 事务回滚
connection.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
}
}
finally {
// 开启自动提交
try {
connection.setAutoCommit(true);
} catch (SQLException e) {
e.printStackTrace();
}
DBUtil.close(prepareStatement);
DBUtil.close(connection);
}
}
}
没有事务机制时
没有事务处理的操作
Connection conn = null;
PreparedStatement prst = null;
Statement stmt = null;
try {
conn = DBUtil.getConnection();
String sql = "insert into test_jdbc (id,name,money) values(?,?,?)";
prst = conn.prepareStatement(sql);
prst.setInt(1, 31);
prst.setString(2, "事务测试1");
prst.setDouble(3, 11.1);
prst.addBatch();
prst.setInt(1, 32);
// 这里故意写成1,让报错
prst.setString(1, "事务测试2");
prst.setDouble(3, 21.1);
prst.addBatch();
prst.setInt(1, 33);
prst.setString(2, "事务测试3");
prst.setDouble(3, 31.1);
prst.addBatch();
prst.executeBatch();
} catch (Exception e) {
e.printStackTrace();
} finally {
DBUtil.close(stmt);
DBUtil.close(prst);
DBUtil.close(conn);
}
插入三条数据,但是第二天添加失败,但是1和3都能添加成功,
因为默认是一条SQL就提交一次
连接池
数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个;
释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏。这项技术能明显提高对数据库操作的性能