本文已参与「新人创作礼」活动,一起开启掘金创作之路。
****什么是JDBC
编辑
JDBC架构
分为两层
JDBC应用程序层:负责管理数据库连接,提供标准的数据库访问接口
JDBC驱动程序层:
驱动程序的类型
JDBC-ODBC桥驱动程序
本机API驱动程序
网络协议驱动程序
本机协议驱动程序
使用JDBC的API
DriverManager类:为数据库加载驱动程序
Driver接口:表示数据库驱动程序,被所有JDBC驱动程序类实现
Connection接口:表示Java应用程序和数据库之间的连接
Statement接口:表示SQL语句
PreparedStatement接口:
ResultSet接口:表示从数据库检索到的数据
SQLException类:表示数据库抛出的异常
使用JDBC的步骤
加载驱动程序
使用驱动程序管理器加载和注册所需的驱动程序,使用到了DriverManager类和Driver接口
DriverManager类中的registerDriver方法或直接Class.forName()
//方法1:
Class.forName("com.mysql.cj.jdbc.Driver");
/*
将类的字节码文件加载到内存
有一些代码会随着类的加载而执行,这些代码就是静态代码块中的
*/
//方法2:
Driver d = new com.mysql.cj.jdbc.Driver();
DriverManager.registerDriver(d);
/*
先创建Driver对象,用registerDriver()方法注册驱动程序
*/
连接到数据库
可以在Java应用程序中创建多个Connection对象来访问不同的数据库,使用Connection接口
DriverManger类中的getConnection()方法
该方法有三个重载
public static Connection getConnection(String url);
//这种方式的url与下面两种的不同
String url = "jdbc:mysql://localhost:3306/company;user = root;password = niit";
public static Connection getConnection(String url, Properties info);
public static Connection getConnection(String url,String user,String password);
//静态方法的目的是能够通过类名直接调用
String url = "jdbc:mysql://localhost:3306/company";
Connection conn = DriverManager.getConnection(url,"root","niit");
创建和执行SQL语句
创建了连接之后,肯定是用来连接的对象来执行SQL语句,所以可以使用Connection对象提供的createStatement()方法来创建Statement对象
创建时用到了Statement接口
Statement sta = conn.createStatement();
创建时还用到了PreparedStatement接口,PreparedStatement接口继承自Statement接口
从名字看它不是直接创建语句,而是在准备SQL语句
步骤
1.先准备一个SQL模板,需要参数的位置用?代替
2.将?补充
3.执行语句,因为sql字符串中有?所以不能将sql字符串传入executeQuery方法中,而是补充好?的模板对象直接调用相应的方法
String sql = "select * from login where username =? and password = ?";
psta = conn.prepareStatement(sql);
psta.setString(1,username);
psta.setString(2,password);
rs = psta.executeQuery();
执行时用到了Statement接口中的excute(sql) 和executeUpdate(sql) 和executeQuery(sql) 方法
excute可以去执行create,alter,drop这些语句
其实excute可以执行任意的sql语句,并返回Boolean值,如果执行结果是ResultSet的对象,该方法返回true,否则返回false
String sql = "create table student(id int,name varchar(20))"
int count = sta.executeUpdate(sql);
if(count > 0)
System.out.println("添加成功");
else
System.out.println("添加失败");
//count接收的是sql语句影响到的数据库中受影响的行数
excuteUpdate可以去执行insert,update,delete这些语句
String sql = "insert into player values (1,123)";
int count = sta.executeUpdate(sql);
if(count > 0)
System.out.println("添加成功");
else
System.out.println("添加失败");
String sql = "update player set ID = 2 where ID=1";
int count = sta.executeUpdate(sql);
//执行sql语句后有返回值,该返回值用来判断执行的情况
if(count > 0)
System.out.println("添加成功");
else
System.out.println("添加失败");
excuteQuery可以去执行select语句
当使用excuteQuery时返回的结果就不用int类型的变量来接收了,这里用到了结果集ResultSet 来接收.
ResultSet rs = null;
String sql = "select * from player";
rs = sta.executeQuery(sql);
//游标默认情况下在第一行数据上面,所以先让游标向下移动一行,才能去获取第一行数据
rs.next();
int id = rs.getInt(1);//取数据,取第一行的第一列数据
System.out.println(id);
/*
这种情况不能确定游标所指的那行有没有数据
所以要改进
*/
ResultSet rs = null;
String sql = "select * from player";
rs = sta.executeQuery(sql);
while(rs.next()) { //判断是否有数据
int id = rs.getInt(1);
String name = rs.getString(2);
System.out.print(id+"\t");
System.out.println(name);
}
结果集有不同的类型,通过创建语句对象Statement sta = conn.createStatement();时确定
各种类型以常量字段形式定义在ResultSet接口中
Statement createStatement();
/*
不接受任何参数,它获取到的ResultSet默认只读,且只允许向前滚动
*/
Statement createStatement(int resultSetType,int resultSetConcurrency);
Statement createStatement(int resultSetType,int resultSetConcurrency,int resultSetHoldability);
/*
resultSetType代表ResultSet类型,它决定结果集指针是可滚动还是仅向前,表格的1,2,3
resultSetConcurrency指定结果集的并发模式,它决定结果集中的数据是可更新还是只读,表格的4,5
resultSetHoldability指定事务提交后,是否继续保留指针,意味着是否可以继续读取数据,表格的6,7
*/
| ResultSet字段 | 描述 |
| TYPE_SCROLL_SENSITIVE | 允许指针双向移动,如果数据发生修改,能及时同步到数据库 |
| TYPE_SCROLL_INSENSITIVE | 允许指针双向移动,如果数据库发生修改,不能及时同步到数据库 |
| TYEPE_FORWARD_ONLY | 只允许指针从前向后移动 |
| CONCUR_READ_ONLY | 不允许更新ResultSet对象的并发模式,也就是只能读 |
| CONCUR_UPDATABLE | 允许更新ResultSet对象的并发模式,也就是可读加可写 |
| CLOSE_CURSOR_AT_COMMIT | 提交当前事务后关闭指针(不能再取到数据) |
| HOLD_CURSOR_OVER_COMMIT | 提交当前事务后保留指针(可以继续使用数据) |
结果集ResultSet中的方法
rs.first();//指针跳转到第一行
rs.isFirst;//指针是否在第一行
rs.beforeFirst;//指针跳转到第一行之前
rs.isBeforeFirst();//指针是否在第一行之前
rs.last();//指针跳转到最后一行
rs.islast;//指针是否在最后一行
rs.afterLast;//指针跳转到最后一行之后
rs.isAfterLast();//指针是否在最后一行之后
rs.previous();//指针跳转到上一行
rs.next();//指针跳转到下一行
rs.absolute(int i);//指针跳转到指定行
rs.relative(int i);//指针向前或向后跳转的相对行数,正数向前跳,负数向后跳
XXX getXXX(String columnLabel);//根据列的名称获取该列的值,并转换为XXX类型
XXX getXXX(int columnIndex);//根据列的位置,获取该列的值,并转换为XXX类型
如
int id = rs.getInt(1);
String name = rs.getString(2);
修改结果集时用到的方法
rs.updateRow();//更新ResultSet对象,同时将数据同步到数据库中对应的表
rs.insertRow();//向ResultSet对象中插入一行数据,同时将数据同步到数据库中对应的表
rs.deleteRow();//从ResultSet对象中删除一行数据,同时在数据库中对应的表中删除该行数据
rs.updateString(int columnIndex,String x);//根据列索引找到字段,并使用指定字符串修改它
rs.updateString(String columnLabel,String x);//根据列名找到字段,并使用指定字符串更新它
rs.updateInt(int columnIndex.int x);//根据列索引找到字段,并使用指定数字修改它
rs.updateInt(String columnLabel,int x);//根据列名找到字段,并使用指定数字更新它
处理SQL异常
我们给数据库发送的SQL语句,执行时可能会出现错误,比如插入数据时主键冲突,此时JDBC会把问题包装成SQLException异常
由于不能确保SQL是否正确,数据库是否能连上,所以JDBC代码必须处理SQLException
完整的使用JDBC代码
public class JDBC_Demo2 {
public static void main(String[] args) {
Statement sta = null;
Connection conn = null;
try {
//注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//定义sql
String sql = "insert into player values (1,123)";
//获取Connection对象
String url = "jdbc:mysql://localhost:3306/company";
conn = DriverManager.getConnection(url,"root","niit");
//获取执行sql的对象
sta = conn.createStatement();
//执行sql
int count = sta.executeUpdate(sql);
//处理结果
System.out.println(count);
if(count > 0)
System.out.println("添加成功");
else
System.out.println("添加失败");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}catch (SQLException e) {
e.printStackTrace();
}finally {
//释放资源
if(sta != null) {
try {
sta.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
public class JDBC_Demo6 {
public static void main(String[] args) {
Connection conn = null;
Statement sta = null;
ResultSet rs = null;
try {
Class.forName("com.mysql.cj.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/company";
conn = DriverManager.getConnection(url,"root","niit");
sta = conn.createStatement();
String sql = "select * from player";
rs = sta.executeQuery(sql);
while(rs.next()) { //判断是否有数据
int id = rs.getInt(1);
String name = rs.getString(2);
System.out.print(id+"\t");
System.out.println(name);
}
rs.first();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}catch (SQLException e) {
e.printStackTrace();
}finally {
if(rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(sta != null) {
try {
sta.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
管理数据库事务
什么是事务
事务作为一个单元执行的一组SQL语句,只有当所有的SQL语句都执行成功,事务才完成,如果任意一个SQL语句执行失败,则整个事务回滚执行前的状态,它的目的是保证数据的一致性。
提交事务
Connection类提供了两种提交事务的方式:
隐式:当一组SQL语句执行完毕后,默认自动提交事务
显式:当一组SQL语句执行完毕后,手动调用commit()方法提交事务,这需要先将提交模式变更为手动,通过Connection类,con.setAutoCommit(false);来变更提交模式为手动
回滚事务
如果在提交之前,事务执行失败,虽然之前的所有SQL语句都没有执行效果,但此时数据库中的表被锁定,导致其他程序无法提交,为了避免这类问题,必须快速回滚,通过Connection类,con.rollback();