JDBC与数据库连接,查询
package jdbc;
import java.sql.*;//数据库相关库
public class TestJDBC {
public static void main(String[] args) {
final String DBDRIVER="org.mariadb.jdbc.Driver";//数据库连接驱动,对于每一种数据库都不一样
final String DBURL="jdbc:mariadb://localhost:3306/test";//数据库地址,3306是端口,test是数据库名
final String DBUSER="root";//数据库用户名
final String DBPASSWORD="root";//数据库密码
Connection conn=null;//数据库连接对象
Statement statement=null;//数据库SQL语言执行语句
ResultSet rs=null;//返回数据结果集合,包含着只想数据的指针
try {
Class.forName(DBDRIVER);//反射机制初始化对象
conn=DriverManager.getConnection(DBURL,DBUSER,DBPASSWORD);//设置数据库连接
statement=conn.createStatement();//statement创建
rs=statement.executeQuery("SELECT * from test1");//statement执行查询语句得到结果传递给rs,test1是表名,下同
while(rs.next()){//ResultSet指针初始指向在数据集第一行的前面,运用.next()方法依次向下查询
System.out.println(rs.getString("name"));//根据字段名获取数据,并以String类型存入内存
}
} catch (ClassNotFoundException e) {//Driver对象创建时引入的错误抛出
e.printStackTrace();
}catch(SQLException e){//Connection对象创建时引入的错误抛出
e.printStackTrace();
}finally{//良好的变成习惯,在使用过数据库操作之后,将ResultSet,Statement,Connection对象都清空
try {
if(rs!=null){
rs.close();
}
if(statement!=null){
statement.close();
}
if(conn!=null){
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
所有实验使用数据库如下:
JDBC插入数据
package jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class TestDML {
public static void main(String[] args) {
final String DBDRIVER="org.mariadb.jdbc.Driver";
final String DBURL="jdbc:mariadb://localhost:3306/test";
final String DBUSER="root";
final String DBPASSWORD="root";
Connection conn=null;
Statement statement=null;
ResultSet rs=null;
try {
Class.forName(DBDRIVER);
conn=DriverManager.getConnection(DBURL,DBUSER,DBPASSWORD);
statement=conn.createStatement();
//和上一个唯一不同是这里使用的是插入操作
String sql="insert into test1 values (4,'James')";//手工写入完整SQL语句
statement.executeUpdate(sql);//statement执行SQL语句
} catch (ClassNotFoundException e) {
e.printStackTrace();
}catch(SQLException e){
e.printStackTrace();
}finally{
try {
if(rs!=null){
rs.close();
}
if(statement!=null){
statement.close();
}
if(conn!=null){
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
从传入参数获取数据的JDBC数据插入
package jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class TestDML2 {
public static void main(String[] args) {
if(args.length!=2){//程序输入参数判断
System.out.println("Parameter Error! Please input again!");
System.exit(-1);
}
int num=0;
try{
num=Integer.parseInt(args[0]);//将第一个传入参数强制转换为Int类型存入num
}catch(NumberFormatException e){//严谨编程,捕捉数据格式错误
e.printStackTrace();
System.out.println("Number Format Error");
System.exit(-1);
}
String name=args[1];//输入参数都是字符串,不需要再转换
final String DBDRIVER="org.mariadb.jdbc.Driver";
final String DBURL="jdbc:mariadb://localhost:3306/test";
final String DBUSER="root";
final String DBPASSWORD="root";
Connection conn=null;
Statement statement=null;
ResultSet rs=null;
try {
Class.forName(DBDRIVER);
conn=DriverManager.getConnection(DBURL,DBUSER,DBPASSWORD);
statement=conn.createStatement();
//使用传入参数的方式,获取需要的插入的数据
//可以发现这种手工组合SQL语言的方式非常繁琐,容易出错
String sql="insert into test1 values ("+num+",'"+name+"')";
System.out.println(sql);//在调试的时候,可以将SQL语句输出进行检查,或者试验在数据库中执行
statement.executeUpdate(sql);//执行更新数据库
} catch (ClassNotFoundException e) {
e.printStackTrace();
}catch(SQLException e){
e.printStackTrace();
}finally{
try {
if(rs!=null){
rs.close();
}
if(statement!=null){
statement.close();
}
if(conn!=null){
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
JDBC预设语句PreparedStatement
package jdbc;
import java.sql.*;
public class TestPreparedStatement {
public static void main(String[] args) {
if(args.length!=2){
System.out.println("Paramenter Error, please input again!");
System.exit(-1);
}
int num=0;
String name=null;
try{
num=Integer.parseInt(args[0]);
}catch(NumberFormatException e){
e.printStackTrace();
System.exit(-1);
}
name=args[1];
final String DBDRIVER="org.mariadb.jdbc.Driver";
final String DBURL="jdbc:mariadb://localhost:3306/test";
final String DBUSER="root";
final String DBPASSWORD="root";
Connection conn=null;
PreparedStatement pStatement=null;
try {
Class.forName(DBDRIVER);
conn=DriverManager.getConnection(DBURL,DBUSER,DBPASSWORD);
//上一个通过手工拼接SQL语句的操作非常的繁琐,有可以解决的方式
//使用PreparedStatement,预先设置好语句的格式,然后一次设置待定的参数
pStatement=conn.prepareStatement("insert into test1 values (?,?)");//?是两个待定的参数
pStatement.setInt(1, num);//将第一个?位置设置为num
pStatement.setString(2, name);//将第二个?位置设置为name
pStatement.executeUpdate();//执行操作
} catch (ClassNotFoundException e) {
e.printStackTrace();
}catch(SQLException e){
e.printStackTrace();
}finally{
try {
if(conn!=null){
pStatement.close();
conn.close();
}
name=null;
num=0;
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
JDBC批量操作Batch
package jdbc;
import java.sql.*;
public class TestBatch {
public static void main(String[] args) throws Exception{
//这个程序没有写像之前那么严谨的try/catch程序块,是为了更清晰地表现逻辑
Connection conn=null;
PreparedStatement ps=null;
Class.forName("org.mariadb.jdbc.Driver");
conn=DriverManager.getConnection("jdbc:mariadb://localhost:3306/test", "root", "root");
ps=conn.prepareStatement("insert into test1 values (?,?)");
//实际工程经常会有大量的数据增删改查,一条一条数据的操作自然不行
//需要批量的操作,JDBC中使用Batch
ps.setInt(1, 3);
ps.setString(2, "Iverson");
ps.addBatch();//将组装好的PreparedStatement存入Batch
ps.setInt(1, 1);
ps.setString(2, "McGrady");
ps.addBatch();
ps.setInt(1,3);
ps.setString(2, "Wade");
ps.addBatch();
ps.executeBatch();//执行Batch,执行三条数据库插入
ps.close();
conn.close();
}
}
JDBC事务
package jdbc;
import java.sql.*;
public class TestTransaction {
public static void main(String[] args) {
Connection conn=null;
Statement statement=null;
try {
Class.forName("org.mariadb.jdbc.Driver");
conn=DriverManager.getConnection("jdbc:mariadb://localhost:3306/test", "root", "root");
//有些数据库操作时必须绑定起来进行的,例如账户转账,从一个账户中扣除,另一个账户中存入,两个操作必须同时成功,或者同时失败,否则会造成数据错误
//对于这种绑定在一起的数据库操作,称之为一个事务
//JDBC中,首先要设置连接不是自动提交的,默认状态是自动提交
conn.setAutoCommit(false);//可以把这句话注释掉,然后在下面其中一句插入语句中制造一点小错误,来试验效果
statement=conn.createStatement();
statement.addBatch("insert into test1 values (21,'Duncan')");
statement.addBatch("insert into test1 values (13,'Nash')");
statement.executeBatch();//执行Batch
//注释与不注释实验看区别
conn.commit();//提交事务
conn.setAutoCommit(true);//事务执行完,将自动提交设置为true
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
try {
if(conn!=null){
conn.rollback();
conn.setAutoCommit(true);
}
} catch (SQLException e1) {
e1.printStackTrace();
}
}finally{
try {
if(statement!=null){
statement.close();
statement=null;
}
if(conn!=null){
conn.close();
conn=null;
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
JDBC处理可滚动的数据结果集
package jdbc;
import java.sql.*;
public class TestScroll {
public static void main(String[] args) {
Connection conn=null;
Statement statement=null;
ResultSet rs=null;
try {
Class.forName("org.mariadb.jdbc.Driver");
conn=DriverManager.getConnection("jdbc:mariadb://localhost:3306/test", "root", "root");
//http://www.cnblogs.com/bukudekong/archive/2011/06/22/2086528.html
statement=conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY);
//设置回滚模式,回滚模式,不仅可以使用.next()进行数据轮询,还可以使用以下功能,实现更灵活的数据结果操作
rs=statement.executeQuery("select * from test1");
rs.next();
System.out.println(rs.getInt("id"));
System.out.println(rs.getString("name"));
rs.last();//最后一行
System.out.println(rs.getInt("id"));
System.out.println(rs.getString("name"));
System.out.println(rs.isLast());//是否是最后一行
System.out.println(rs.isAfterLast());//是否在最后一行之后
System.out.println(rs.getRow());//当前行数
rs.previous();//前一行
System.out.println(rs.getInt("id"));
System.out.println(rs.getString("name"));
System.out.println(rs.isLast());
System.out.println(rs.getRow());//当前行数
rs.absolute(6);//绝对位置,第6行
System.out.println(rs.getInt("id"));
System.out.println(rs.getString("name"));
rs.first();//第一行
System.out.println(rs.getInt("id"));
System.out.println(rs.getString("name"));
System.out.println(rs.isFirst());
}catch(ClassNotFoundException e){
e.printStackTrace();
}catch(SQLException e){
e.printStackTrace();
}finally{
try {
if(rs!=null){
rs.close();
rs=null;
}
if(statement!=null){
statement.close();
statement=null;
}
if(conn!=null){
conn.close();
conn=null;
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
JDBC处理可更新的数据集
package jdbc;
import java.sql.*;
public class TestUpdatetable {
public static void main(String[] args) {
Connection conn=null;
Statement statement=null;
ResultSet rs=null;
try {
Class.forName("org.mariadb.jdbc.Driver");
conn=DriverManager.getConnection("jdbc:mariadb://localhost:3306/test", "root", "root");
//http://www.cnblogs.com/bukudekong/archive/2011/06/22/2086528.html
statement=conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_UPDATABLE);
rs=statement.executeQuery("select * from test1");
//updatetable可以使java逻辑和数据库实时关联,不需要提交就可以实时更改数据
//但这需要数据库驱动支持CONCUR_UPDATABLE模式
//但貌似并不是所有数据库都支持,mariadb貌似不支持
//猜想应该是处于安全性的考虑,大的自由度也带来了较大的风险
rs.next();
rs.updateInt("id", 8);
rs.updateRow();//更新行数据
}catch(ClassNotFoundException e){
e.printStackTrace();
}catch(SQLException e){
e.printStackTrace();
}finally{
try {
if(rs!=null){
rs.close();
rs=null;
}
if(statement!=null){
statement.close();
statement=null;
}
if(conn!=null){
conn.close();
conn=null;
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
本文是参考尚学堂马士兵的JDBC教学视频编写得来,讲得很清楚,适合初学。
新增:
深入
1 、 PreparedStatement 接口继承 Statement , PreparedStatement 实例包含已编译的 SQL 语句,所以其执行速度要快于 Statement 对象。
2 、作为 Statement 的子类, PreparedStatement 继承了 Statement 的所有功能。三种方法
execute 、 executeQuery 和 executeUpdate 已被更改以使之不再需要参数
3 、在 JDBC 应用中 , 如果你已经是稍有水平开发者 , 你就应该始终以 PreparedStatement 代替
Statement. 也就是说 , 在任何时候都不要使用 Statement.
基于以下的原因 :
一 . 代码的可读性和可维护性 .
虽然用 PreparedStatement 来代替 Statement 会使代码多出几行 , 但这样的代码无论从可读性还是可维护性上来说 . 都比直接用 Statement 的代码高很多档次 :
stmt.executeUpdate(“insert into tb_name (col1,col2,col2,col4) values (‘”+var1+”’,’”+var2+”’,”+var3+”,’”+var4+”’)”);//stmt 是 Statement 对象实例
perstmt = con.prepareStatement(“insert into tb_name (col1,col2,col2,col4) values (?,?,?,?)”);
perstmt.setString(1,var1);
perstmt.setString(2,var2);
perstmt.setString(3,var3);
perstmt.setString(4,var4);
perstmt.executeUpdate(); //prestmt 是 PreparedStatement 对象实例
不用我多说 , 对于第一种方法 . 别说其他人去读你的代码 , 就是你自己过一段时间再去读 , 都会觉得伤心 .
PreparedStatement 尽最大可能提高性能 .
语句在被 DB 的编译器编译后的执行代码被缓存下来 , 那么下次调用时只要是相同的预编译语句就不需要编译 , 只要将参数直接传入编译过的语句执行代码中 ( 相当于一个涵数 ) 就会得到执行 . 这并不是说只有一个 Connection 中多次执行的预编译语句被缓存 , 而是对于整个 DB 中 , 只要预编译的语句语法和缓存中匹配 . 那么在任何时候就可以不需要再次编译而可以直接执行 . 而 statement 的语句中 , 即使是相同一操作 , 而由于每次操作的数据不同所以使整个语句相匹配的机会极小 , 几乎不太可能匹配 . 比如 :
insert into tb_name (col1,col2) values (‘11’,’22’);
insert into tb_name (col1,col2) values (‘11’,’23’);
即使是相同操作但因为数据内容不一样 , 所以整个个语句本身不能匹配 , 没有缓存语句的意义 . 事实是没有数据库会对普通语句编译后的执行代码缓存 .
当然并不是所以预编译语句都一定会被缓存 , 数据库本身会用一种策略 , 比如使用频度等因素来决定什么时候不再缓存已有的预编译结果 . 以保存有更多的空间存储新的预编译语句 .
三 . 最重要的一点是极大地提高了安全性 .
即使到目前为止 , 仍有一些人连基本的恶义 SQL 语法都不知道 .
String sql = “select * from tb_name where name= ‘”+varname+”’ and passwd=’”+varpasswd+”’”;
如果我们把 [’ or ‘1’ = ‘1] 作为 varpasswd 传入进来 . 用户名随意 , 看看会成为什么 ?
select * from tb_name = ’ 随意 ’ and passwd = ” or ‘1’ = ‘1’;
因为 ‘1’=’1’ 肯定成立 , 所以可以任何通过验证 . 更有甚者 :
把 [‘;drop table tb_name;] 作为 varpasswd 传入进来 , 则 :
select * from tb_name = ’ 随意 ’ and passwd = ”;drop table tb_name; 有些数据库是不会让你成功的 , 但也有很多数据库就可以使这些语句得到执行 .
而如果你使用预编译语句 . 你传入的任何内容就不会和原来的语句发生任何匹配的关系 . 只要全使用预编译语句 , 你就用不着对传入的数据做任何过虑 . 而如果使用普通的 statement, 有可能要对 drop,; 等做费尽心机的判断和过虑 .