概念:
Java DataBase Connectivity Java 数据库连接, Java语言操作数据库
JDBC本质:其实是官方(sun公司)定义的一套操作所有关系型数据库的规则,即接口。各个数据库厂商去实现这套接口,提供数据库驱动jar包。我们可以使用这套接口(JDBC)编程,真正执行的代码是驱动jar包中的实现类。
起使jdbc就是将java操作数据库建立起联系
使用Jdbc的好处
1) 程序员如果要开发访问数据库的程序,只需要会调用 JDBC 接口中的方法即可,不用关注类是如何实现的。 2) 使用同一套 Java 代码,进行少量的修改就可以访问其他 JDBC 支持的数据库
JDBC 的核心 API
JDBC的使用
在项目中新建立一个module然后在module中建立一个包libs存放导入的jar包 然后将jdbc的jar包拷贝进去 然后 右击选择ADD as Library 即可使用此jar包了
导入驱动 Jar 包
创建jdbc练级的步骤
1. 导入驱动jar包 mysql-connector-java-5.1.37-bin.jar 2. 注册驱动 3. 获取数据库连接对象 Connection 4. 定义sql * 注意:sql的参数使用?作为占位符。 如:select * from user where username = ? and password = ?; 5. 获取执行sql语句的对象 PreparedStatement Connection.prepareStatement(String sql) 6. 给?赋值: * 方法: setXxx(参数1,参数2) * 参数1:?的位置编号 从1 开始 * 参数2:?的值 7. 执行sql,接受返回结果,不需要传递sql语句 8. 处理结果 9. 释放资源
首先我们要使用Class.forName(数据库驱动) 来加载对应的数据库驱动
然后建库数据库连接DriverManager.Connection(数据库信息)
然后建立sql语句
再建立PrepareStatemane来进行sql语句的执行
然后再释放资源即可
eg:
public void Tuesday(){ Connection conn = null; PreparedStatement state = null; try { //1 :注册驱动 Class.forName("com.mysql.jdbc.Driver"); //2.获取连接 conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/girls","zhao","123456"); //3:建立sql语句 String sql = "update admin set username = 'Test' where username =?"; //4.获取执行sql的对象 state = conn.prepareStatement(sql); //5:sql补全 state.setString(1,"testOne"); //6:执行sql state.execute(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } finally { //7:资源释放 if(state != null){ try { state.close(); } catch (SQLException e) { e.printStackTrace(); } } if(conn != null){ try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } }
DriverManager:驱动管理对象
功能:
1: 1. 注册驱动:告诉程序该使用哪一个数据库驱动jar 内部源码: static void registerDriver(Driver driver) :注册与给定的驱动程序 DriverManager 。 通过查看源码发现:在com.mysql.jdbc.Driver类中存在静态代码块 static { try { java.sql.DriverManager.registerDriver(new Driver()); } catch (SQLException E) { throw new RuntimeException("Can't register driver!"); } } 2. 获取数据库连接: * 方法:static Connection getConnection(String url, String user, String password) * 参数: * url:指定连接的路径 * 语法:jdbc:mysql://ip地址(域名):端口号/数据库名称 * 例子:jdbc:mysql://localhost:3306/db3 * 细节:如果连接的是本机mysql服务器,并且mysql服务默认端口是3306,则url可以简写为:jdbc:mysql:///数据库名称 * user:用户名 * password:密码
.数据库连接的url:
- JDBC URL 用于标识一个被注册的驱动程序,驱动程序管理器通过这个 URL 选择正确的驱动程序,从而建立到数据库的连接。 - JDBC URL的标准由三部分组成,各部分间用冒号分隔。 - jdbc:子协议:子名称 - 协议:JDBC URL中的协议总是jdbc - 子协议:子协议用于标识一个数据库驱动程序 - 子名称:一种标识数据库的方法。子名称可以依不同的子协议而变化,用子名称的目的是为了定位数据库提供足够的信息。包含主机名(对应服务端的ip地址),端口号,数据库名
几种常见的url编写方式
- MySQL的连接URL编写方式: - jdbc:mysql://主机名称:mysql服务端口号/数据库名称?参数=值&参数=值 - jdbc:mysql://localhost:3306/atguigu - jdbc:mysql://localhost:3306/atguigu?useUnicode=true&characterEncoding=utf8(如果JDBC程序与服务器端的字符集不一致,会导致乱码,那么可以通过参数指定服务器端的字符集) - jdbc:mysql://localhost:3306/atguigu?user=root&password=123456 - Oracle 9i的连接URL编写方式: - jdbc:oracle:thin:@主机名称:oracle服务端口号:数据库名称 - jdbc:oracle:thin:@localhost:1521:atguigu - SQLServer的连接URL编写方式: - jdbc:sqlserver://主机名称:sqlserver服务端口号:DatabaseName=数据库名称 - jdbc:sqlserver://localhost:1433:DatabaseName=atguigu
Connection:数据库连接对象
1. 功能: 1. 获取执行sql 的对象 * Statement createStatement() * PreparedStatement prepareStatement(String sql) 2. 管理事务: * 开启事务:setAutoCommit(boolean autoCommit) :调用该方法设置参数为false,即开启事务 * 提交事务:commit() * 回滚事务:rollback()
Connection就是我们定义的数据库连接对象 连接之后的对象在去找到执行sql的对象去执行sql
Statement:执行sql的对象(不推荐使用)
因为Statement会导致sql注入问题所以不推荐使用
1. 执行sql 1. boolean execute(String sql) :可以执行任意的sql 了解 2. int executeUpdate(String sql) :执行DML(insert、update、delete)语句、DDL(create,alter、drop)语句 * 返回值:影响的行数,可以通过这个影响的行数判断DML语句是否执行成功 返回值>0的则执行成功,反之,则失败。 3. ResultSet executeQuery(String sql) :执行DQL(select)语句
sql注入问题:
1. SQL注入问题:在拼接sql时,有一些sql的特殊关键字参与字符串的拼接。会造成安全性问题 1. 输入用户随便,输入密码:a' or 'a' = 'a 2. sql:select * from user where username = 'fhdsjkf' and password = 'a' or 'a' = 'a' 这样就会提示输入正确因为or是某一个成立即可 后面的会成立就代表成功了
Statement因为会导致sql注入问题我们不推荐使用而是使用PrepareStatement来解决sql注入问题
PreparedStatement (来解决sql注入)
特征
1:预编译的SQL: SQL 语句被预编译并存储在此对象中,可以使用此对象多次高效地执行该语句。 2:参数使用?作为占位符 reparedStatement 对象所代表的 SQL 语句中的参数用问号(?)来表示,调用 PreparedStatement 对象的 setXxx() 方法来设置这些参数.
setXxx() 方法有两个参数,第一个参数是要设置的 SQL 语句中的参数的索引(从 1 开始),第二个是设置的 SQL 语句中的参数的值 3: 给?赋值 方法: setXxx(参数1,参数2) * 参数1:?的位置编号 从1 开始 * 参数2:?的值 例如我们给一个sql中的类型为varchar的对应的?和int的?分别赋值,语句中应该有两个sql varchar在前int对应的在后面就是 preparestatement.setString(1,"值"); prepareStatement.setInt(2,1234); 4: 执行sql,接受返回结果,不需要传递sql语句 执行preparestatement.execute()即可执行语句
创建PrepareStatement
可以通过调用 Connection 对象的 preparedStatement(String sql) 方法获取 PreparedStatement 对象
1: 代码的可读性和可维护性。 2: PreparedStatement 能最大可能提高性能: - DBServer会对预编译语句提供性能优化。因为预编译语句有可能被重复调用,所以语句在被DBServer的编译器编译后的执行代码被缓存下来,
那么下次调用时只要是相同的预编译语句就不需要编译,只要将参数直接传入编译过的语句执行代码中就会得到执行。 - 在statement语句中,即使是相同操作但因为数据内容不一样,所以整个语句本身不能匹配,没有缓存语句的意义.事实是没有数据库会对普通语句编译后的执行代码缓存。这样每执行一次都要对传入的语句编译一次。 - (语法检查,语义检查,翻译成二进制命令,缓存) PreparedStatement 可以防止 SQL 注入
SQL类型 | |
---|---|
boolean | BIT |
byte | TINYINT |
short | SMALLINT |
int | INTEGER |
long | BIGINT |
String | CHAR,VARCHAR,LONGVARCHAR |
byte array | BINARY , VAR BINARY |
java.sql.Date | DATE |
java.sql.Time | TIME |
java.sql.Timestamp | TIMESTAMP |
ResultSet:结果集对象,封装查询结果
当是更新插入删除操作时使用prepareStatement.execute() 进行操作 但是查询操作就是获取的结果集就需要用ResultSet来获取结果集
* boolean next(): 游标向下移动一行,判断当前行是否是最后一行末尾(是否有数据),如果是,则返回false,如果不是则返回true * getXxx(参数):获取数据 * Xxx:代表数据类型 如: int getInt() , String getString() * 参数: 1. int:代表列的编号,从1开始 如: getString(1) 2. String:代表列名称。 如: getDouble("balance") * 注意: * 使用步骤: 1. 游标向下移动一行 2. 判断是否有数据 3. 获取数据 //循环判断游标是否是最后一行末尾。 while(rs.next()){ //获取数据 //6.2 获取数据 int id = rs.getInt(1); String name = rs.getString("name"); double balance = rs.getDouble(3); System.out.println(id + "---" + name + "---" + balance); }
* resultSet 在获取的时候最初是的是获取的是数据库的表中的第一列的名称就是每一列的字段名 需要使用nex才能获取出每一列下面的值 * next 游标向下移动一行并判断是否有数据 如果没有数据返回false有数据返回true * 我们在使用next的时候要先判断是否有数据再继续使用next去获取值
// ResultSet 获取结果集 public void SpringOne(){ Connection conn = null; PreparedStatement state = null; ResultSet set = null; try { Class.forName("com.mysql.jdbc.Driver"); conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/girls","zhao","123456"); String sql = "select id from admin where username = ? "; state = conn.prepareStatement(sql); state.setString(1,"July"); set = state.executeQuery(); while (set.next()){ int id = set.getInt("id"); System.out.println(id); } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } finally { if(set != null){ try { set.close(); } catch (SQLException e) { e.printStackTrace(); } } if(state != null){ try { state.close(); } catch (SQLException e) { e.printStackTrace(); } } if(conn != null){ try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } }
eg: 判断用户是否登陆成功
public class AugustOneSummerOne { public static void main(String[] args) { Scanner sc = new Scanner(System.in); System.out.println("请输入用户名:"); String user = sc.nextLine(); System.out.println("请输入密码:"); String pwd = sc.nextLine(); boolean status = new AugustOneSummerOne().login(user,pwd); if(status){ System.out.println("登陆成功"); }else { System.out.println("用户名或密码失败"); } } public boolean login(String username,String pwd){ Connection conn = null; PreparedStatement state = null; ResultSet set = null; try { if(username == null || pwd == null){ return false; } Class.forName("com.mysql.jdbc.Driver"); conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/girls","zhao","123456"); String sql = "select * from admin where username = ? and password =?"; state = conn.prepareStatement(sql); state.setString(1,username); state.setString(2,pwd); set = state.executeQuery(); return set.next(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } finally { if(set != null){ try { set.close(); } catch (SQLException e) { e.printStackTrace(); } } if(state != null){ try { state.close(); } catch (SQLException e) { e.printStackTrace(); } } if(conn != null){ try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } return false; } }
创建jdbc工具类
我们上面的数据库连接都写在了每一个连接内这样不便于代码的维护 每一个的关闭资源和获取资源有很多事重复的也可以提取出来
首先我们在当权module下建立一个配置文件(必须当前使用的module/src 当前module下的src内下不是module下的src外部),因为是配置文件我们就以.properties来结尾
配置文件:
url=jdbc:mysql://数据库地址:数据库端口/要链接的库 user=用户名 password=密码 driver=com.mysql.jdbc.Driver eg: url=jdbc:mysql:///girls user=zhao password=123456 driver=com.mysql.jdbc.Driver
工具类的module下的src目录新建立Util然后在内部再创建工具类
package cn.itcast.util; import java.io.FileReader; import java.io.IOException; import java.net.URL; import java.sql.*; import java.util.Properties; /** * JDBC工具类 */ public class JDBCUtils { private static String url; private static String user; private static String password; private static String driver; /** * 文件的读取,只需要读取一次即可拿到这些值。使用静态代码块 */ static{ //读取资源文件,获取值。 try { //1. 创建Properties集合类。 Properties pro = new Properties(); //获取src路径下的文件的方式--->ClassLoader 类加载器 ClassLoader classLoader = JDBCUtils.class.getClassLoader(); //当前类名.class.ClassLoader URL res = classLoader.getResource("jdbc.properties"); String path = res.getPath(); // System.out.println(path);///D:/IdeaProjects/itcast/out/production/day04_jdbc/jdbc.properties //2. 加载文件 // pro.load(new FileReader("D:\IdeaProjects\itcast\day04_jdbc\src\jdbc.properties")); pro.load(new FileReader(path)); //3. 获取数据,赋值 url = pro.getProperty("url"); user = pro.getProperty("username"); password = pro.getProperty("password"); driver = pro.getProperty("driver"); //4. 注册驱动 Class.forName(driver); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } /** * 获取连接 * @return 连接对象 */ public static Connection getConnection() throws SQLException { return DriverManager.getConnection(url, user, password); } /** * 释放资源 * @param stmt * @param conn */ public static void close(Statement stmt,Connection conn){ if( stmt != null){ try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } } if( conn != null){ try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } /** * 释放资源 * @param stmt * @param conn */ public static void close(ResultSet rs,Statement stmt, Connection conn){ 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(); } } } }
然后再内部使用的时候可以直接调用我们生成的工具类进行数据库的连接和资源的关闭,当数据库变动的时候只需要改动配置文件即可,这样对代码也有很好的维护性
事务操作
事务操作
1. 事务:一个包含多个步骤的业务操作。如果这个业务操作被事务管理,则这多个步骤要么同时成功,要么同时失败。 2. 操作: 1. 开启事务 2. 提交事务 3. 回滚事务 3. 使用Connection对象来管理事务 * 开启事务:setAutoCommit(boolean autoCommit) :调用该方法设置参数为false,即开启事务 * 在执行sql之前开启事务 * 提交事务:commit() * 当所有sql都执行完提交事务 * 回滚事务:rollback() * 在catch中回滚事务
public static void main(String[] args) { Connection conn = null; PreparedStatement stateOne =null; PreparedStatement stateTwo = null; try { Class.forName("com.mysql.jdbc.Driver"); conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/girls","zhao","123456"); //开启事务 conn.setAutoCommit(false); //2 :定义sql String sql = "update admin set password = ? where id =?"; String sqlOne = "update admin set password = ? where id = ?"; //3: 执行sql stateOne = conn.prepareStatement(sql); stateTwo = conn.prepareStatement(sqlOne); //4: 设置参数 stateOne.setString(1,"500"); stateOne.setInt(2,21); stateTwo.setString(1,"500"); stateTwo.setInt(2,22); //5: 执行sql stateOne.executeUpdate(); stateTwo.executeUpdate(); //6:提交事务 conn.commit(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } finally { if(stateTwo != null){ try { stateTwo.close(); } catch (SQLException e) { e.printStackTrace(); } } if(stateOne != null){ try { stateOne.close(); } catch (SQLException e) { e.printStackTrace(); } } if(conn != null){ try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } }
通用的操作
//通用的增删改操作 public void update(String sql,Object ...args){//sql中占位符的个数与可变形参的长度相同! Connection conn = null; PreparedStatement ps = null; try { //1.获取数据库的连接 conn = JDBCUtils.getConnection(); //2.预编译sql语句,返回PreparedStatement的实例 ps = conn.prepareStatement(sql); //3.填充占位符 for(int i = 0;i < args.length;i++){ ps.setObject(i + 1, args[i]);//小心参数声明错误!! } //4.执行 ps.execute(); } catch (Exception e) { e.printStackTrace(); }finally{ //5.资源的关闭 JDBCUtils.closeResource(conn, ps); } }
/** * * @Description 针对于不同的表的通用的查询操作,返回表中的一条记录 * @author shkstart * @date 上午11:42:23 * @param clazz * @param sql * @param args * @return */ public <T> T getInstance(Class<T> clazz,String sql, Object... args) { Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; try { conn = JDBCUtils.getConnection(); ps = conn.prepareStatement(sql); for (int i = 0; i < args.length; i++) { ps.setObject(i + 1, args[i]); } rs = ps.executeQuery(); // 获取结果集的元数据 :ResultSetMetaData ResultSetMetaData rsmd = rs.getMetaData(); // 通过ResultSetMetaData获取结果集中的列数 int columnCount = rsmd.getColumnCount(); if (rs.next()) { T t = clazz.newInstance(); // 处理结果集一行数据中的每一个列 for (int i = 0; i < columnCount; i++) { // 获取列值 Object columValue = rs.getObject(i + 1); // 获取每个列的列名 // String columnName = rsmd.getColumnName(i + 1); String columnLabel = rsmd.getColumnLabel(i + 1); // 给t对象指定的columnName属性,赋值为columValue:通过反射 Field field = clazz.getDeclaredField(columnLabel); field.setAccessible(true); field.set(t, columValue); } return t; } } catch (Exception e) { e.printStackTrace(); } finally { JDBCUtils.closeResource(conn, ps, rs); } return null; }
.