JDBC
JDBC概念
通过java代码操作数据库,实现在java代码中对数据库进行增删改查。
JDBC相关的类
DriverManager(驱动管理者)
getConnection()方法获取连接对象
-
第一个参数:连接的数据库名称
-
第二个参数:数据库的用户名
-
第三个参数:数据库的密码
Connection(连接对象)
- createStatement()方法创建语句对象
- 管理事务(ACID):原子性,一致性,隔离性,持久性
- 开启事务 setAutoCommit(),方法中是个布尔类型的参数,false表示手动提交(开启事务)
- 提交事务Commit()
- 回滚事务rollback()一般用在catch处理异常的代码中,即异常时回滚数据。
Statement(语句对象)
- executeUpdate(这个方法是操作数据库中数据的增删改)
- executeQuery(操作数据库中的查询)
两个方法的参数都是一条sql语句,即字符串类型,executeUpdate返回值是int类型的,返回的内容是影响数据库表中数据的行数。executeQuery的返回值是一个ResultSet(结果集对象)。
ResultSet(结果集对象)
作用:封装了数据库响应回来的数据
- next()可以理解为这个方法为一个指针,一开始指向的是表中第一行数据的上方(不是字段名,即字段名和第一行数据的中间位置),返回值是boolean类型,如果为true,则表示有数据,如果为false,则表示没有数据。
- getXXX(String s)和next()一起使用,如果next()返回的是true,调用getXXX(String s)可以获得这一行的数据,里面的参数就传这一行你想要的数据的字段名类型,XXX就是对应的数据类型。如,我想查这一行信息中年龄的信息,getInt(“age”),返回值仍然是Int。
JDBC使用注意事项及步骤
1.导入jar包(JDBC的具体实现),连接哪个数据库就用哪个数据库的驱动jir包
2.获取连接 用DriverManager中getConnection方法
3.利用Connection中的createStatement创建语句对象
4.通过语句对象实现对数据库的增删改查,增删改返回值为int(影响行数),查询的返回值为ResultSet(结果集对象)
5.利用ResultSet中的next和getXXX方法解析结果集
6.释放资源 colse();先开后关
JDBC获取连接代码
public static void main(String[] args) throws Exception{
//1.导入jar包
//2.获取连接
Connection con=DriverManager.getConnection
("jdbc:用的数据库,如mysql://连接数据库地址/数据库名称","数据库账号","数据库密码");
//3.创建语句对象
Statement stat = con.createStatement();
//4.在控制台看到连接对象的结果
System.out.println(con);
JDBC进行增删改操作(以增为例子,三种操作是一样的只有sql语句不一样)
public static void main(String[] args) throws Exception{
//1.导入jar包
//2.获取连接
Connection con = DriverManager.getConnection("jdbc:如mysql://连接数据库地址/具体的哪个库","数据库账号","数据库密码");
//3.利用连接对象获取语句对象
Statement stat = con.createStatement();
//4.定义一个sql语句为字符串类型
String sql = "insert into 表名 values(值)";
//5.语句对象进行增删改操作(增删改的方法名为executeUpdate),返回值为影响的行数
int rs = stat.executeUpdate(sql);//传入的参数是上面定义的sql语句
//6.在控制台打印影响的行数
System.out.println("影响的行数为:"+rs);
//7.释放资源
stat.close();
con.close();
}
JDBC进行查询操作
public static void main(String[] args) throws Exception{
//1.导入jar包
//2.获取连接
Connection con = DriverManager.getConnection("jdbc:如mysql://连接数据库地址/具体的哪个库","数据库账号","数据库密码");
//3.利用连接对象获取语句对象
Statement stat = con.createStatement();
//4.定义一个sql语句为字符串类型
String sql = "select * from 表名";
//5.语句对象进行查询操作(查询的方法名为executeQuery),返回值为一个结果集
ResultSet rs = stat.executeQuery(sql);//传入的参数是上面定义的sql语句
//6.解析结果集,while循环,如果指针下有数据就获取出来循环打印
while (rs.next()){
int id = rs.getInt("id");//比如这个表中第一列字段名为id的int类型的编号
String username = rs.getString("username");//第二列字段名为username的字符串类型的用户名
String password = rs.getString("password");//第三列字段名为password的字符串类型的密码
//7.打印第一行的数据
System.out.println(id+"/t"+username+"/t"+password);//如果表中有多行数据下一次进入循环,以此类推
}
//7.释放资源
rs.close();
stat.close();
con.close();
}
注意事项
- 要导入数据库驱动的jar包
- sql语句不能有错误
- 连接地址,账户,密码不能写错
- 最后要关闭资源,先开后关
sql注入
在用户用网页登录时,利用特殊的sql格式来实现错误密码的登录,最终在java代码中正确的查询到了数据,导致登录成功。
如用户在用户名处写 ‘ ’ or 1 = 1 – ,就算输入密码,也能登录成功。
‘ ’ or 1 = 1 – 这条语句通过java代码,带到数据库中的显示就是select * from 表名 where loginname=‘ ’ or 1 = 1 --,–在mysql中代表注释的意思,后面的内容可以忽略不记,所以这条语句在mysql中是可以查询成功的,所以导致sql注入问题。
解决方案
PreparedStatement接口
1.为什么要使用PreparedStatement
通过继承接口可以知道,他的父接口是Statement(语句对象),既然父类解决不了sql注入问题,就要使用它的子类来解决sql注入。
2.PreparedStatement执行原理
sql语句会预先编译,在创建语句对象的时候提供了sql语句,而不是等到执行的时候才提供sql语句,执行效率会更高,sql语句代码所有要替换的参数使用占位符,占位符是问号(?)
3.PreparedStatement的优点
- sql语句会预先编译,执行效率会更高。
- 解决了sql注入的问题,更安全
- sql语句代码的可读性更好,替换的参数变成了占位符
PreparedStatement中相关的方法
1.如何得到这个对象
通过连接对象创建预编译语句对象,创建的时候要提供sql语句,语句可以有占位符,可以没有,如果有占位符,后面要替换占位符为真实的值。
2.executeUpdate和executeQuery
预编译语句对象中也有这两个方法,但是在使用的时候不用提供sql语句,因为创建的时候提供了sql语句,最主要的是怎样替换占位符
3.怎样替换占位符
insert into student values(?,?,?),比如这条sql语句,预编译语句对象为ps,那么替换第一个值就是ps.setXXX(1,替换的内容),以此类推,替换的内容要以数据库中的表的数据结构一致
public static void main(String[] args) throws Exception{
//1.导入jar包
//2.获取连接
Connection con = DriverManager.getConnection("jdbc:如mysql://连接数据库地址/具体的哪个库","数据库账号","数据库密码");
//3.利用连接对象获取预编译语句对象
PreparedStatement ps = con.prepareStatement("insert into student values(?,?,?)");//这里的问号就是占位符
//4.替换占位符,如果第一个为ind,第二个为String,第三个为String
ps.setInt(1,1);//表示为第一个占位符赋值1
ps.setString(2,"zhangsan");
ps.setString(3,"zhangsan123");
//5.替换占位符后,告诉预编译对象你要进行的操作
int rs = ps.executeUpdate();//这里是增加数据,所以是update
//6.可以直接关闭资源,不看影响的行数
ps.close();
con.close();
}
事务
- 开启事务
Connection对象中setAutoCommit(false)
- 提交事务
Connection中commit()
- 回滚事务
Connection中rollback();
public static void main(String[] args) throws Exception{
//因为后面要释放资源,所以定义在外面
Connection con = null;
PreparedStatement ps = null;
try{
con = DriverManager.getConnection("jdbc:如mysql://连接数据库地址/具体的哪个库","数据库账号","数据库密码");
//开启事务
con.setAutoCommit(false);
//模拟转账操作
ps = con.prepareStatement("update user set money = money-500 where name='zhangsan'");
ps = con.prepareStatement("update user set money = money+500 where name='lisi'");
ps.executeUpdate();
//如果没有报错,提交事务
con.commit();
}catch (Exception e){
//如果报错,回滚事务
con.rollback();
e.printStackTrace();
}finally {
//释放资源,进行非空的判断,如果不为空就关
if(ps != null){
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (con != null){
try {
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}