JDBC基础入门
JDBC(Java DataBase Connectivity),就是java数据库连接。
得到connection对象
要使用JDBC,要先做以下4个步骤:
1. 导入jar包
2. 加载驱动类
3. 准备好url、username、password
4. 用DriverManager类得到connection对象
首先要导入jar包,这jar包也叫驱动。oracle、mysql等等每一个都有不同的jar包,例如mysql的就叫做mysql-connector-java-版本号。
加载驱动类,可用
Class.forname("com.mysql.jdbc.Driver")
来完成,因为主要是加载里面的静态代码块,因此可以利用反射来完成。接下来便是准备url、username、password。
url:jdbc:mysql://localhost:3306/数据库名
username:数据库用户名
password:数据库密码最后便是用DriverManager来得到connection对象。
Connection con = DriverManager.getConnection(url,username,password);
总结起来便是在导入jar包后,跑下面的语句
Class.forName("com.mysql.jdbc.Driver");//数据库驱动名字
String url = "jdbc:mysql://localhost:3306/xxx";//数据库协议地址
String username = xxx;//数据库用户名
String password = xxx;//数据库密码
Connection con = DriverManager.getConnection(url,username,password);
若是oracle,只需要修改上面第一二条语句。
Class.forName("oracle.jdbc.Driver.OracleDriver");
String url = "jdbc:oracle:thin:@localhost:1521:xxx";
在最后要关闭Connection类对象,即执行
con.close();
增、删、查、改操作
至此,已经得到了connection对象了。有了这个对象,便可以对数据库进行增、删、查、改的操作了。
示例如下
Statement stmt = con.createStatement();
String add = "insert into coolBoy values('喝风少年', '18', 'male', '你就别说我了吧')";
String mod = "update coolBoy set age='19', sign='生日快乐啊' where name='喝风少年'";
String del = "delete from coolBoy";
String query = "select * from coolBoy";
stmt.excuteUpdate(add);
stmt.excuteUpdate(mod);
stmt.excuteUpdate(del);
stmt.excuteQuery(query);
- 最后Statement需要关闭,即
stmt.close();
- excuteUpdate()返回值是int型,指的是操作了多少行记录。
- excuteQuery()返回值是ResultSet类对象:
下一行:
rs.next();
总行数:
rs.last();
int rowCount = rs.getRow();
总列数:
int columnCount = rs.getMetaData().getColumnCount();
获取属性值:
while(rs.next()){
rs.getString(0);//取第一列数据
rs.getString("id");//取列名为"id"列的数据
}
SQL攻击
但是Statement容易受到sql攻击,因此要使用PreparedStatement(预编译声明)来替代Statement。
- 那么什么是sql攻击呢?
即用户输入的参数与DAO中的SQL语句合成一个完整的SQL语句。
例如用户输入了账号username、密码password登录。
public static boolean login(String username, String password){
Statement stmt = con.createStatement();
String query =
"select * from user where username='" + username + "' and password='" + password + "'";
ResultSet rs = stmt.excuteQuery(query);
return rs.next();
}
如果用户这时候的输入是
username = ” a' or 'a'='a
“;
password = ” a' or 'a'='a
“;
那么用户就能登录成功。
PreparedStatement的使用
public static boolean login(String username, String password){
String query = "select * from coolBoy where username=? and sex=?";
PreparedStatement pstmt = con.preparedStatement(query);//创建模版
pstmt.setString(1, username);//给第一个问号赋值
pstmt.setString(2, password);//给第二个问号赋值
ResultSet rs = pstmt.excuteQuery();
return rs.next();
}
- 最后PreparedStatement需要关闭,即
pstmt.close();
- 赋值还有setInt(),setDouble()等等方法。
- 每个preparedStatement都与一个SQL模版绑定,先把SQL模版传递给数据库,数据库先校验语法,再进行编译,执行时只是把参数传递过去而已,因此可以防SQL攻击。
- 同时也提高了可读性和效率(开启预编译后,对同一句SQL不需要二次校验和编译,直接传参)。
预编译
默认使用PreparedStatement是不能执行预编译的,需要在url中给出
useServerPrepStmts = true
参数才开启。
例如
String url = "jdbc:mysql://localhost:3306/xxx?useServerPrepStmts=true";
这样才能保证mysql驱动先把sql语句发送给服务器进行预编译,然后在执行excuteQuery()时只是把参数发送给服务器。当使用不同的PreparedStetement执行相同的sql语句时,还是会出现编译两次的现象,只是因为驱动没有缓存编译后的函数key。这就需要设置
cachePrepStmts = true
。即
String url = "jdbc:mysql://localhost:3306/xxx?useServerPrepStmts=true&cachePrepStmts=true"
。
批处理
当有多条sql语句要执行时,一次向服务器发送一条sql语句,这么做效率会很差。这时候需要使用批处理,一次向服务器发送多条sql语句,由服务器一次性处理。
要开启批处理,同样需要在url中添加参数
rewriteBatchedStatements=true
。再加上开启预编译,总结一起,mysql的数据库协议地址应该为
String url = "jdbc:mysql://localhost:3306/xxx?rewriteBatchedStatements=true&useServerPrepStmts=true&cachePrepStmts=true"
批处理只关系到增、删、改操作,查不需要批处理。
可以多次调用PreparedStatement类的addBatch()方法,把需要执行的所有sql语句添加到一个批中,然后调用PreparedStatement的excuteBatch()方法来执行批中的语句。
当执行了批之后,批中的语句就会被清空。所以当连续调用excuteBatch()的时候,也只是调用了一次,因为执行第一次后,批中已经没有sql语句了。当然也可以使用clearBatch()来清空批。
要注意的是,在添加批之前,应该把自动提交设为false,
con.setAutoCommit(false)
,这也表示事务的开始,在处理完批之后,进行手动提交con.commit()
,完成所有操作后,把自动提交还原为true,con.setAutoCommit(true)
,这样在异常发生的时候,就可以执行回滚,con.rollback()
,撤销数据库中已完成的部分操作,回滚到事务开始状态。