JAVA中的JDBC
JDBC
-
概念: Java DataBase Connectivity (java数据库链接),java语言操作数据库
-
本质:其实就是官方(sun公司)定义的一套操作所有操作关系型数据库的规则(接口),由各个数据库厂商去实现这套接口,提供jar包,我们可以使用这套(jdbc)接口进行编程,真正实行代码的是驱动jar包中的实现类
-
快速入门
- 步骤
- 导入驱动jar包:mysql-connector-java
- 复制jar包到项目的libs目录下
- 右键 add as llibrary
- 注册驱动
- 获取数据库链接对象 Connection
- 定义sql
- 获取执行sql语句对象 Statement
- 执行sql,接受返回结果
- 处理结果
- 释放资源
- 导入驱动jar包:mysql-connector-java
代码:
public static void main(String[] args) { Connection conn = null; Statement stmt = null; try { /*注册驱动*/ Class.forName("com.mysql.jdbc.Driver"); /*获取链接*/ conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbc", "root", "root"); /*定义sql*/ String sql = "delete from jdbc where id = 1"; /*获取执行sql对象*/ stmt = conn.createStatement(); /*执行sql,返回值为影响的行数*/ int i = stmt.executeUpdate(sql); if (i > 0) { System.out.println("删除成功"); } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } finally { /*释放资源*/ if (stmt!=null){ try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } } if (stmt!=null){ try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } } }
- 步骤
-
详解各个常用对象
-
DriverManager:驱动管理对象
-
功能:
-
注册驱动:告诉程序该使用哪一个数据库的驱动jar包
大家可能有疑问:注册驱动不是Class.forName(“com.mysql.jdbc.Driver”)?为什么DriverManager可以注册驱动?那么他们有什么关系呢?
在DriverManager类下有一个方法registerDriver(),他可以注册驱动
static void registerDriver(Driver driver): /*注册与给定的驱动程序 写代码使用Class.forName("com.mysql.jdbc.Driver"); */
我们再来想一下Class.forName()被调用就可以完成注册驱动的动作,那么有什么方式可以随着类的加载而自动执行,回答是肯定的,静态代码块
带着疑问我们再来查看Driver源码 发现:在Driver类中果然存在静态代码块
static { try { DriverManager.registerDriver(new Driver()); } catch (SQLException var1) { throw new RuntimeException("Can't register driver!"); } }
由此可以看出真正执行注册驱动的动作是 DriverManager.registerDriver(new Driver()),我们写Class.forName(“com.mysql.jdbc.Driver”)比较简单
注意:在mysql5版本之后注册驱动可以省略
-
获取数据链接:
-
方法:getConnection()
-
参数:
-
url:指定连接路径
语法(每个数据库语法不同,以mysql为例):jdbc:mysql://ip(域名):端口号/数据库名称
如果链接本机mysql服务器,并且mysql默认端口3306,则url可以省略为:jdbc:mysql:///数据库名称
-
user:用户名
-
password:密码
-
-
-
-
-
Connection:数据库链接对象
- 功能:获取执行sql对象
- Statement createStatement()
- PreparedStatement prepareStatement(String sql)
- 管理事务:
- 开启事务:setAutoCommit(boolean autoCommit) --调用该方法设置参数为false,即开启事务
- 提交事务:commit()
- 回滚事务rollback()
- 功能:获取执行sql对象
-
Statement:执行sql对象
- 执行sql常用方法
- boolean execute(String sql):可以执行任意sql
- int executeUpdate(String sql) :执行DML语句,DDL语句,返回值为int表示影响的行数,可以通过这个返回值判断sql是否执行成功
- ResultSet executeQuery(String sql) :执行DQL语句
- 执行sql常用方法
-
ResultSet:结果集对象,封装查询结果
-
boolean next():将游标向下移动一行,判断当前行是否为最后一行末尾(是否有数据),如果是最后一行返回flase,反之返回turn
ResultSet对象牵扯到游标的概念(类似指针),游标看做默认指向表头(列名行),要想取出ResultSet对象中查询到的数据,需要将游标向下移动,每移动一行取出一行的数据需要用到next方法:
-
getXxx(参数):获取数据
- Xxx表示数据类型 如:getInt,getString…
- 参数:
- int:代表列的编号(数字代表第几列)
- String:代表列名称
-
注意:
- 使用步骤:
- 游标向下移动一行
- 判断是否有数据
- 获取数据
public static void main(String[] args) { Connection conn = null; Statement stmt = null; ResultSet rs = null; try { /*注册驱动*/ Class.forName("com.mysql.jdbc.Driver"); /*获取链接*/ conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbc", "root", "root"); /*定义sql*/ String sql = "select * from user"; /*获取执行sql对象*/ stmt = conn.createStatement(); /*执行sql*/ rs = stmt.executeQuery(sql); while (rs.next()) { int uid = rs.getInt("uid"); String username = rs.getString("username"); int money = rs.getInt("money"); System.out.println("uid: " + uid + "username: " + username + "money: " + money); } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } finally { /*释放资源*/ if (rs != null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if (stmt != null) { try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } } if (conn != null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } }
- 使用步骤:
为了之后使用方便创建一个工具类JdbcUtils
public class JdbcUtils { private static String url; private static String user; private static String password; private static String driver; static { try { Properties pro = new Properties(); ClassLoader classLoader = JdbcUtils.class.getClassLoader(); InputStream is = classLoader.getResourceAsStream("jdbc.properties"); pro.load(is); url = pro.getProperty("url"); user = pro.getProperty("user"); password = pro.getProperty("password"); driver = pro.getProperty("driver"); Class.forName(driver); } catch (Exception e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(url, user, password); } public static void close(Connection conn, Statement stat, ResultSet rs) { if (rs != null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if (stat != null) { try { stat.close(); } catch (SQLException e) { e.printStackTrace(); } } if (conn != null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } }
-
PreparedStatement:执行sql对象
-
sql注入问题:在拼接sql时,有一些sql的特殊关键字参与字符串的拼接,会造成安全问题
-
解决sql注入问题:使用PreparedStatement对象
-
预编译的sql:参数使用?作为占位符
-
步骤
-
导入驱动jar包:mysql-connector-java
- 复制jar包到项目的libs目录下
- 右键 add as llibrary
-
注册驱动
-
获取数据库链接对象 Connection
-
定义sql
注意:sql的参数使用?作为占位符
如:select * from user where id = ?
-
获取执行sql语句对象 PreparedStatement
Connection.preparedStatement(String sql)
-
给?赋值:
方法: setXxx(参数1,参数2)
参数1:?的位置编号,从1开始
参数2:?的值
-
执行sql,接受返回结果,不需要传递参数
-
处理结果
-
释放资源
-
-
注意:后期都会使用preparedStatement来完成赠三改查的所有操作
- 可以防止sql注入
- 效率更高
public static void main(String[] args) { User user = new User(); Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; try { /*获取链接*/ conn = JdbcUtils.getConnection(); /*获取预编译执行sql对象*/ ps = conn.prepareStatement("select * from user where uid = ?"); /*为?赋值*/ ps.setInt(1,1); /*执行sql*/ rs = ps.executeQuery(); if (rs.next()) { /*获取查询信息*/ int uid = rs.getInt("uid"); String username = rs.getString("username"); int money = rs.getInt("money"); /*将信息存入user对象*/ user.setUid(uid); user.setUsername(username); user.setMoney(money); /*打印控制台*/ System.out.println(user); } } catch (SQLException e) { e.printStackTrace(); } finally { /*释放资源*/ JdbcUtils.close(conn, ps, rs); }
如果查询结果为多条,可以使用while循环来获取/存入数据
-
Jdbc控制事务
-
事务:一个包含多个步骤的业务操作,如果这个业务操作被事务管理,则这多个步骤要么同时成功,要么同时失败
/** * 转账的案例 * * @param args */ public static void main(String[] args) { Connection conn = null; PreparedStatement ps1 = null; PreparedStatement ps2 = null; try { conn = JdbcUtils.getConnection(); /*修改zhangsan用户金额减500*/ ps1 = conn.prepareStatement("update user set money = money + ? where username = ?"); ps1.setInt(1, -500); ps1.setString(2, "zhangsan"); /*修改lisi用户金额加500*/ ps2 = conn.prepareStatement("update user set money = money + ? where username = ?"); ps2.setInt(1, 500); ps2.setString(2, "lisi"); /*执行sql*/ ps1.executeUpdate(); /*设置一个异常*/ int i = 1 / 0; ps2.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); }finally { /*释放资源*/ JdbcUtils.close(conn,ps1,null); JdbcUtils.close(null,ps2,null); } }
通过代码发现:zhangsan用户金额已减,但是lisi却没有增加,我们期望的结果:如果转账过程中出现任何异常,所有用户的数据不发生改变,如果其中一个发生改变就都会改变.那么我们需要事务的控制
-
操作:
- 开启事务
- 提交事务
- 回滚事务
-
使用Connection对象来管理事务
- 开启事务:setAutoCommit(boolean autoCommit) --调用该方法设置参数为false,即开启事务
- 在执行sql之前开启事务
- 提交事务:commit()
- 当所有sql都执行完之后
- 回滚事务:rollback()
- 在catch中回滚事务
/** * 转账的案例 * * @param args */ public static void main(String[] args) { Connection conn = null; PreparedStatement ps1 = null; PreparedStatement ps2 = null; try { conn = JdbcUtils.getConnection(); /*开启事务*/ conn.setAutoCommit(false); /*修改zhangsan用户金额减500*/ ps1 = conn.prepareStatement("update user set money = money + ? where username = ?"); ps1.setInt(1, -500); ps1.setString(2, "zhangsan"); /*修改lisi用户金额加500*/ ps2 = conn.prepareStatement("update user set money = money + ? where username = ?"); ps2.setInt(1, 500); ps2.setString(2, "lisi"); /*执行sql*/ ps1.executeUpdate(); /*设置一个异常*/ int i = 1 / 0; ps2.executeUpdate(); /*提交事务*/ conn.commit(); } catch (SQLException e) { try { /*回滚事务*/ conn.rollback(); } catch (SQLException e1) { e1.printStackTrace(); } e.printStackTrace(); }finally { /*释放资源*/ JdbcUtils.close(conn,ps1,null); JdbcUtils.close(null,ps2,null); } }
- 开启事务:setAutoCommit(boolean autoCommit) --调用该方法设置参数为false,即开启事务