驱动问题这里不再详述,接下的就是建立连接,建立连接最基本的方法是直接采取DriverManager直接连接数据库。但是这种方法缺陷很大,建立连接的开销很大,当有多个用户请求连接时,如果为每个用户都建立一次连接,且单个用户建立的连接在使用完毕之后直接被废弃,这样的开销是不可想象的。
//DriverManager有两个作用,注册驱动还有获取连接
// jdbc:mysql://ip:端口/数据库名称?参数键值对1&参数键值对2
//连接本机数据库时可以不写IP和端口
//可以通过禁用安全连接来解决报错警告,useSSL = false,等号前后不要加入空格
//prepareStatement预编译功能必须要在url中首先开启useServerPrepStmts=true
//Connection可以连接数据库,执行sql语句并返回结果
//DriverManager.getConnection会被连接池所取代
Connection conn = DriverManager.getConnection(url,userName,password);
为了解决该问题,我们采取连接池技术来取代DriverManager.getConnection()。 我使用的连接池为Druid,一个阿里的开源连接池技术。
//Druid连接演示1.导入jar,2.完善配置文件,配置文件放在src下面3.加载配置,但是需要根据需求改
//加载配置文件
Properties prop = new Properties();
//文件路径可能出现问题,可以使用System.getProperty()来查看当前文件位置,并对文档路径进行修正
System.out.println("path="+System.getProperty("user.dir"));
prop.load(new FileInputStream("JDBC_demo/src/druid.properties"));
//获取连接池对象
DataSource dataSource = DruidDataSourceFactory.createDataSource(prop);
//获取连接
Connection conn = dataSource.getConnection();
配置文件具体内容如下
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql:///test?useSSL=false&useServerPrepStmts=true
username=root
password=//自己数据库的密码
# 初始连接数
initialSize=5
# 最大连接数
maxActive=10
# 最大连接等待时间
maxWait=3000
接下来是定义sql,和获取执行sql对象,这两步息息相关。
//Connection中有三种方法来获取执行对象
//createStatement():最为普通的获取sql对象的方法
//prepareStetement():预编译sql的执行对象,防止sql注入
//prepareCall():执行存储过程的对象,该方法不常用,本文也不做讲解。
如果采取最普通的createStatement(),那么直接完整的写出sql就可以,但是存在着被sql注入的风险。
name = "'or '1' = '1";//sql注入演示,通过截断密码来绕过密码判断
sql3 = "select * from account where id ="+id+" and name = '"+name+"'";
如果想要避免这种情况,可以采用prepareStetement()方法。sql语句的定义也要随之改变。
sql3 = "select * from account where name = ? and id = ?";
PreparedStatement pstmt = conn.prepareStatement(sql3);
Statement stmt = conn.Statement();
//在给prepareStatement()方法时,就会进行检查SQL语法和编译SQL这两步,剩下的只剩执行SQL语句
//sql中的?需要单独设置
pstmt.setString(1,name);
pstmt.setInt(2,1);
//如果多次执行该语句,那么只需要对?部分进行更换即可,不需要再次进行语法检查和编译,只需要进行执行
//PreparedStatement()会对内部值进行转译,将'变成\',恢复其原本的含义,防止字符串拼接
//PreparedStatement()还可以通过预编译来提高速度,提高性能
sql语句在MySQL中执行时可以分为三步,1.检查语法2.编译语句3.执行SQL,采取该方法可以进行预编译,节省大量时间,同时要在上文中的配置文件中使用useServerPrepStmts=true开启预编译功能。 同样的,在执行SQL语句这一步,两种方法也有着一些不同之处: PreparedStatement pstmt = conn.prepareStatement(sql3);以及获取完毕SQL语句,所以直接执行就行,当然,在执行之前需要先获取执行对象。
PreparedStatement pstmt = conn.prepareStatement(sql3);
Statement stmt = conn.Statement();
res = pstmt.executeQuery();
而createStatement()需要在执行时传入sql
res = stmt.executeQuery(sql);
同时,JDBC也能使用数据库事务
//Connection还可以用来进行事务管理
//事务本身分为开启事务:begin/start transaction
//提交事务:commit
//回滚事务:rollback
//Connection中定义了三个对应的方法
//开启事务:conn.setAutoCommit(bollean autoCommit);autoCommit为是否自动提交事务,false则为手动提交
//提交事务:conn.commit();
//回滚事务:conn.rollback();
可以使用try——catch来配合事务
try{
conn.setAutoCommit(false);
//执行sql
//成功则提交事务
conn.commit();
}catch (){
//失败则回滚事务
conn.rollback();
}
然后是必备的释放资源环节
res.close();//查询返回的结果集合也要释放
stmt.close();
conn.close();
接下来是整体代码
package mysql;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import com.mysql.jdbc.Driver;
//使用数据库连接池可以做到1.资源重用2.提升系统响应速度3.避免连接遗漏(将本来申请的但是不用的连接分配给新的用户)
import javax.sql.DataSource;
import java.io.FileInputStream;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
public class JDBC {
public static void main(String[] args) throws Exception{
//用List来保存查询后的数据
List<User> qur_res = new ArrayList<>();
//注册驱动,这行代码可以不写了
Class.forName("com.mysql.jdbc.Driver");
//获取连接
//DriverManager有两个作用,注册驱动还有获取连接
// jdbc:mysql://ip:端口/数据库名称?参数键值对1&参数键值对2
//连接本机数据库时可以不写IP和端口
//可以通过禁用安全连接来解决报错警告,useSSL = false,等号前后不要加入空格
//prepareStatement预编译功能必须要在url中首先开启useServerPrepStmts=true
String url = "jdbc:mysql://127.0.0.1:3306/test?useSSL=false&useServerPrepStmts=true";
String userName = "root";//
String password = "123456";
//Connection可以连接数据库,执行sql语句并返回结果
//DriverManager.getConnection会被连接池所取代
// Connection conn = DriverManager.getConnection(url,userName,password);
//Druid连接演示1.导入jar,2.完善配置文件,配置文件放在src下面3.加载配置,但是需要根据需求改
//加载配置文件
Properties prop = new Properties();
//文件路径可能出现问题,可以使用System.getProperty()来查看当前文件位置,并对文档路径进行修正
System.out.println("path="+System.getProperty("user.dir"));
prop.load(new FileInputStream("JDBC_demo/src/druid.properties"));
//获取连接池对象
DataSource dataSource = DruidDataSourceFactory.createDataSource(prop);
//获取连接
Connection conn = dataSource.getConnection();
//定义sql
String sql1 = "update account set money = 2000 where name = '张三'";
String sql2 = "select * from account";
//Connection中有三种方法来获取执行对象
//createStatement():最为普通的获取sql对象的方法
//prepareStetement():预编译sql的执行对象,防止sql注入
//prepareCall():执行存储过程的对象,该方法不常用
//Connection还可以用来进行事务管理
//事务本身分为开启事务:begin/start transaction
//提交事务:commit
//回滚事务:rollback
//Connection中定义了三个对应的方法
//开启事务:conn.setAutoCommit(bollean autoCommit);autoCommit为是否自动提交事务,false则为手动提交
//提交事务:conn.commit();
//回滚事务:conn.rollback();
//Statement分为两种,第一种为executeUpdate(sql),负责执行DML和DDL语句,也即增删改和创建,权限变更等操作
//一种为executeQuery(sql),负责执行查询语句
Statement stmt = null;
ResultSet res = null;
int count = 0;
try {//可以用try——catch来完成事务回滚
conn.setAutoCommit(false);
stmt = conn.createStatement();
//执行sql1,返回值为受影响行数
count = stmt.executeUpdate(sql1);
//ResultSet为执行查询语句后返回的结果,封装了DQL查询语句的结果,其实就是保存查询返回的集合
//boolean next() 1.将光标向下移动一行 2.判断当前行是否为有效行
//getxxx(参数) 获取数据,比如getInt,参数可以为int,表示为列的编号,从1开始,或者给出列的名称
res = stmt.executeQuery(sql2);//res保存返回的部分表
//利用循环遍历res表
while(res.next()){
User user = new User();
int id = res.getInt(1);
String name = res.getString(2);
Double money = res.getDouble(3);
System.out.println(id+","+name+","+money);
user.setId(id);
user.setName(name);
user.setMoney(money);
qur_res.add(user);
}
System.out.println(qur_res.get(0).getId());
int id = 1;
String name = "zhang";
String sql3 = "select * from account where id ="+id+" and name ='"+name+"'";
res = stmt.executeQuery(sql3);
if(res.next()){
System.out.println("win");
}else {
System.out.println("lose");
}
Thread.sleep(10000);
name = "'or '1' = '1";//sql注入演示,通过截断密码来绕过密码判断
sql3 = "select * from account where id ="+id+" and name = '"+name+"'";
System.out.println("sql3="+sql3);
res = stmt.executeQuery(sql3);
if(res.next()){
System.out.println("win");
}else {
System.out.println("lose");
}
//可以使用prepareStatement来防止sql注入
sql3 = "select * from account where name = ? and id = ?";
//同一个连接可以更换执行方式
//MySQL的执行过程包括三步1.检查SQL语法,2.编译SQL,3.执行SQL
PreparedStatement pstmt = conn.prepareStatement(sql3);
//在给prepareStatement()方法时,就会进行检查SQL语法和编译SQL这两步,剩下的只剩执行SQL语句
//sql中的?需要单独设置
pstmt.setString(1,name);
pstmt.setInt(2,1);
//如果多次执行该语句,那么只需要对?部分进行更换即可,不需要再次进行语法检查和编译,只需要进行执行
//PreparedStatement()会对内部值进行转译,将'变成',恢复其原本的含义,防止字符串拼接
//PreparedStatement()还可以通过预编译来提高速度,提高性能
res = pstmt.executeQuery();
System.out.println("PreparedStatement:");
if(res.next()){
System.out.println("win");
}else {
System.out.println("lose");
}
//int i = 3/0;//人为的制作异常
conn.commit();
pstmt.close();
//自动给出的异常类为SQLException,人为造异常的时候给出更大的异常类的Exception
} catch (Exception throwables) {
conn.rollback();
throwables.printStackTrace();
}
//释放资源
System.out.println(count);
res.close();
stmt.close();
conn.close();
}
}