07Java之JDBC

本文详细介绍了Java JDBC的使用,包括JDBC基本概念、快速入门、DriverManager、Connection、Statement、PreparedStatement、ResultSet、事务控制、连接池(C3P0和Druid实现)以及Spring JDBC的使用方法,旨在帮助读者深入理解和掌握JDBC操作数据库的各项功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. JDBC简介

1. JDBC基本概念

Java DataBase Connectivity简称JDBC是Java数据库连接, Java语言操作数据库。JDBC本质:其实是官方(sun公司)定义的一套操作所有关系型数据库的规则,即接口。各个数据库厂商去实现这套接口,提供数据库驱动jar包。我们可以使用这套接口(JDBC)编程,真正执行的代码是驱动jar包中的实现类。

在这里插入图片描述

2. JDBC快速入门

  • 步骤:
    1. 导入驱动jar包
    2. 注册驱动
    3. 获取数据库连接对象 Connection
    4. 定义sql
    5. 获取执行sql语句的对象 Statement
    6. 执行sql,接受返回结果
    7. 处理结果
    8. 释放资源

导入驱动jar包

首先:在项目下创建libs目录,然后把jar报辅助到该目录下
在这里插入图片描述然后:右键–>Add As Library;将jar包解压:效果如下
在这里插入图片描述
代码实现

public class Demo01 {
    public static void main(String[] args) throws Exception {
        // 注册驱动
        Class.forName("com.mysql.jdbc.Driver");
        // 获取数据库连接对象
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "root");
        // 定义sql语句
        String sql = "SELECT  * FROM  USER";
        // 获取执行sql语句的对象 Statement
        Statement stmt = conn.createStatement();
        //6.执行sql
        ResultSet set = stmt.executeQuery(sql);
        //7.遍历结果集合
        int i=1;
        while (set.next()) {
            String id = set.getString("id");
            String username = set.getString("username");
            String birthday = set.getString("birthday");
            String sex = set.getString("sex");
            String address = set.getString("address");
            System.out.println(id+":"+username+":"+birthday+":"+sex+":"+address);
        }
        //8.释放资源
        stmt.close();
        conn.close();
    }
}

Class.forName是什么?这是反射的东西,可以简单理解为将该字符串路径下的类加载到内存中

遍历Set集合中的getString是通过数据库中的数据名称来一一对应实现的

2. JDBC各个功能

逻辑框架图
在这里插入图片描述

1. DriverManager(驱动管理对象)

功能

1.注册驱动,通过静态代码块实现(因为驱动只注册一次)

 
 使用反射机制,直接将类加载到内存中去
 Class.forName("com.mysql.jdbc.Driver"); 
 

注意:mysql5之后的驱动jar包可以省略注册驱动的步骤。

2.获取数据库连接

public static Connection getConnection(String url, String user, String password) 方法

参数

  • url:指定连接的路径(格式:jdbc:mysql://ip地址(域名):端口号/数据库名称 )
  • user:用户名
  • password:密码

2. Connection(数据库连接对象)

功能

1.获取执行sql的对象

  • Statement createStatement() 该方法返回一个Statement对象。(没有防止SQL注入功能)
  • PreparedStatement prepareStatement(String sql) 该方法返回预编译的Statement对象,即将SQL语句提交到数据库进行预编译。(有防止SQL注入功能)

2.管理事务

  • 开启事务:void setAutoCommit(boolean autoCommit) :调用该方法设置参数为false,即开启事务
  • 提交事务:void commit()
  • 回滚事务:void rollback()

3.Statement(执行sql的对象)

执行sql

  • boolean execute(String sql) :该方法可以执行任何SQL语句,如果执行后第一个结果是ResultSet对象,则返回true;如果执行后第一个结果为受影响的行数或没有任何结果,则返回false;一般不用这个方法。
  • int executeUpdate(String sql) :执行DML(insert、update、delete)语句、DDL(create,alter、drop)语句
    返回值:影响的行数,可以通过这个影响的行数判断DML语句是否执行成功 返回值>0的则执行成功,反之,则失败。
  • ResultSet executeQuery(String sql) :执行DQL(select)语句

boolean execute(String sql);需要特别注意,当我使用该方法查询不存在的数据是,它依旧返回true,这是因为,查询返回的结果集合是一个ResultSet对象,但是ResultSet是从表头开始的,而表头一定存在,所以结果一定为true,不管是否真的存在该数据,所以一般情况下不要用这个方法

4.PreparedStatement(执行sql的对象)

  1. SQL注入问题:在拼接sql时,有一些sql的特殊关键字参与字符串的拼接,是逻辑本身出现错误,导致整个SQL语句逻辑改变
    1. 输入用户随便,输入密码:sd’ or ‘sd’ = 'sd
    2. sql:select * from user where username = ‘fhdsjkf’ and password = ‘a’ or ‘a’ = ‘a’ 后面的 or ‘a’ = ‘a’始终都为true因此密码逻辑始终正确,导致不需要正确用户名和密码都可以实现登录

举例:用户输入用户名和密码 ,如果数据库中有,则显示登录成功,如果没有,则显示登录失败

public class Demo01 {
    public static void main(String[] args) throws Exception {
        // 注册驱动
        Class.forName("com.mysql.jdbc.Driver");
        // 获取数据库连接对象
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "root");
        // 定义sql语句


        System.out.println("请输入用户名");
        String user = new Scanner(System.in).next();
        System.out.println("请输入密码");
        String password = new Scanner(System.in).next();
        String sql = "select * from s1 where user = '"+user+"' and passord = '"+password+"' ";
        // 获取执行sql语句的对象 Statement
        Statement stmt = conn.createStatement();
        //6.执行sql
        boolean execute = stmt.execute(sql);
        if(execute==true){
            System.out.println("密码输入正确");
        }else {
            System.out.println("密码输入错误");
        }
        //8.释放资源
        stmt.close();
        conn.close();
    }
}

数据库中用户
在这里插入图片描述

当我们输入正确的用户名与密码时
在这里插入图片描述
当我们使用sql注入时
在这里插入图片描述
在这里插入图片描述

很明显,没有用户名与密码都没有也可以登录,只有保证or后面的逻辑是正确即可登录

解决方法
使用PreparedStatement对象;
代码示例

5. ResultSet(结果集)

  • boolean next(): 游标向下移动一行,判断当前行是否是最后一行末尾(是否有数据),如果是,则返回false,如果不是则返回true
  • getXxx(参数): 获取数据
    • Xxx:代表数据类型 如: int getInt() , String getString()

    • 参数:
      1. int:代表列的编号,从1开始 如: getString(1)
      2. String:代表列名称。 如: getDouble(“balance”)

数据库中的数据
在这里插入图片描述
代码实现

 String sql = "SELECT * FROM s1 WHERE id = '2'";
        // 获取执行sql语句的对象 Statement
        Statement stmt = conn.createStatement();
        //6.执行sql
        ResultSet set = stmt.executeQuery(sql);
        while (set.next()){
            // 第一列,int类型数据
            int id = set.getInt(1);
            // 第二列,String类型数据
            String name = set.getString(2);
            // 第三列,String类型数据
            String passwod = set.getString(3);
            System.out.println(id+" "+name+" "+passwod);
        }

输出结果
在这里插入图片描述

3. JDBC控制事务

事务:一个包含多个步骤的业务操作。如果这个业务操作被事务管理,则这多个步骤要么同时成功,要么同时失败。例如 A给B转账,A账户减少,B账户一定要增加,这两个步骤一定要同步,不能一个成功,一个失败。

实现步骤
使用Connection对象来管理事务

  1. 开启事务:使用setAutoCommit(boolean autoCommit)方法 :调用该方法设置参数为false,即开启事务(在执行sql之前开启事务)

  2. 提交事务:commit() (当所有sql都执行完提交事务)

  3. 回滚事务:rollback() (在catch中回滚事务,既事务回到最开始的地方(开启事务时))

代码示例

public class Demo03 {
    public static void main(String[] args) throws Exception {
        
        Connection con =null;
        PreparedStatement p1=null;
        PreparedStatement p2=null;
        
        try {
            Class.forName("com.mysql.jdbc.Driver");
             con = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "root");
            //开启事务
            con.setAutoCommit(false);

            // 定义sql转账语句
            String sql1 = "update money set moneys = moneys - ? where ID = ?";
            String sql2 = "update money set moneys = moneys + ? where ID = ?";

            //预处理sql语句获得PreparedStatement对象
             p1 = con.prepareStatement(sql1);
             p2 = con.prepareStatement(sql2);

            // 设置参数
            //p1
            p1.setDouble(1,20);
            p1.setInt(2,1);
            //p2
            p2.setDouble(1,20);
            p2.setInt(2,2);

            // 执行sql语句
            p1.executeUpdate();
            p2.executeUpdate();
            //提交事务
            con.commit();

        }
        catch (Exception e){
            //事务回滚
            con.rollback();
        }finally {
            p2.close();
            p1.close();
            con.close();
        }
    }
}

执行前数据库
在这里插入图片描述
执行后数据库
在这里插入图片描述

如果我们在第一个转账数据后面添加一个异常

           // 执行sql语句
            p1.executeUpdate();
            手动制造异常
            int i = 3/0;
            p2.executeUpdate();
            //提交事务
            con.commit();

执行一下代码,并不会出现一个减少,一个不变的情况

4. JDBC连接池

基本概念:JDBC连接池其实就是一个容器(集合),存放数据库连接的容器。当系统初始化好后,容器被创建,容器中会申请一些连接对象,当用户来访问数据库时,从容器中获取连接对象,用户访问完之后,会将连接对象归还给容器。

1. C3P0实现

C3P0:数据库连接池技术

步骤:
1. 导入jar包 (两个) c3p0-0.9.5.2.jar mchange-commons-java-0.2.12.jar 。( 不要忘记导入数据库驱动jar包)
2. 定义配置文件:
* 名称: c3p0.properties 或者 c3p0-config.xml
* 路径:直接将文件放在src目录下即可。
3. 创建核心对象:数据库连接池对象 ComboPooledDataSource
4. 获取连接: getConnection
5. 归还连接对象。 Connection.close();

1. 导入jar包

2. 定义配置文件
将c3p0.properties 或者 c3p0-config.xml文件复制到src目录下,然后配置修改参数。

c3p0-config.xml文件

<c3p0-config>
  <!-- 使用默认的配置读取连接池对象 -->
  <default-config>
  	<!--  连接参数 -->
    <property name="driverClass">com.mysql.jdbc.Driver</property>
    <property name="jdbcUrl">jdbc:mysql://localhost:3306/db4</property>
    <property name="user">root</property>
    <property name="password">root</property>
    
    <!-- 连接池参数 -->
    <!--初始化申请的连接数量-->
    <property name="initialPoolSize">5</property>
    <!--最大的连接数量-->
    <property name="maxPoolSize">10</property>
    <!--超时时间-->
    <property name="checkoutTimeout">3000</property>
  </default-config>

  <named-config name="otherc3p0"> 
    <!--  连接参数 -->
    <property name="driverClass">com.mysql.jdbc.Driver</property>
    <property name="jdbcUrl">jdbc:mysql://localhost:3306/db3</property>
    <property name="user">root</property>
    <property name="password">root</property>
    
    <!-- 连接池参数 -->
    <property name="initialPoolSize">5</property>
    <property name="maxPoolSize">8</property>
    <property name="checkoutTimeout">1000</property>
  </named-config>
</c3p0-config>

配置上面的参数,与自己连接的数据库参数一致

  • driverClass
  • jdbcUrl
  • user
  • password

注意:上面的c3p0-config.xml文件,发现有2个配置信息,这是因为c3p0可以一个文件,配置多个连接配置方案。

当我们创建连接池对象时,什么都不传递时,使用的是默认的配置信息,当传递一个named-config name="otherc3p0"信息,就使用于其对应的配置信息

3. 创建核心对象
数据库连接池对象 ComboPooledDataSource

使用默认配置信息

         /  创建数据库连接池对象DataSource
        DataSource pool = new ComboPooledDataSource();

使用其他配置信息

DataSource pool = new ComboPooledDataSource("otherc3p0");

4. 获取连接对象

  Connection conn = pool.getConnection();

5. 归还连接对象

conn,close();

全部代码

public class Demo04C3P0 {
    public static void main(String[] args) throws Exception {
        //创建数据库连接池对象DataSource
        DataSource pool = new ComboPooledDataSource("otherc3p0");

        Connection conn = pool.getConnection();
        String sql = "SELECT * FROM s1 WHERE id = '2'";
        // 获取执行sql语句的对象 Statement
        Statement stmt = conn.createStatement();
        //执行sql
        ResultSet set = stmt.executeQuery(sql);
        while (set.next()){
            // 第一列,int类型数据
            int id = set.getInt(1);
            // 第二列,String类型数据
            String name = set.getString(2);
            // 第三列,String类型数据
            String passwod = set.getString(3);
            System.out.println(id+" "+name+" "+passwod);
        }
       
        stmt.close();
        conn.close();
    }
}

2. Druid实现

Druid实现数据库连接池实现技术,由阿里巴巴提供的

步骤

  1. 导入jar包 druid-1.0.9.jar
  2. 定义配置文件:
    * 是properties形式的
    * 可以叫任意名称,可以放在任意目录
  3. 加载配置文件。Properties
  4. 获取数据库连接池对象:通过工厂来来获得DruidDataSourceFactory
  5. 获取连接对象:getConnection
  6. 归还连接对象: close方法
    1. 导入jar包 druid-1.0.9.jar

2. 定义配置文件

properties文件

driverClassName=com.mysql.jdbc.Driver (注册驱动路径)
url=jdbc:mysql://localhost:3306/test  (数据库连接路径)
username=root  (登录名)
password=root  (密码)
initialSize=5  (初始连接个数)
maxActive=10  (最大连接个数)
maxWait=3000  (等待3000毫秒)

注意括号里面的不能有,空格也不行

3. 加载配置文件 Properties

        /加载配置文件
        Properties prop = new Properties();
        /**
         * 注意,这里使用的Java反射机制
         */
        InputStream is = Demo05.class.getClassLoader().getResourceAsStream("druid.properties");
        prop.load(is);

4.获取连接池对象

       //4.获取连接池对象
	        DataSource ds = DruidDataSourceFactory.createDataSource(prop);

5.获取连接对象

 Connection con = ds.getConnection();

6.归还连接对象:

 con.close();

5. Spring JDBC

Spring框架对JDBC的简单封装。提供了一个JDBCTemplate对象简化JDBC的开发

步骤

  1. 导入jar包
  2. 创建JdbcTemplate对象。依赖于数据源DataSource
    * JdbcTemplate template = new JdbcTemplate(ds);
  3. 调用JdbcTemplate的方法来完成CRUD的操作

不同方法

  • update():执行DML语句。增、删、改语句
  • queryForMap():查询结果将结果集封装为map集合,将列名作为key,将值作为value 将这条记录封装为一个map集合
    * 注意:这个方法查询的结果集长度只能是1
  • queryForList():查询结果将结果集封装为list集合
    * 注意:将每一条记录封装为一个Map集合,再将Map集合装载到List集合中
  • query():查询结果,将结果封装为JavaBean对象
    * query的参数:RowMapper
    * 一般我们使用BeanPropertyRowMapper实现类。可以完成数据到JavaBean的自动封装
    * new BeanPropertyRowMapper<类型>(类型.class)
  • queryForObject:查询结果,将结果封装为对象
    * 一般用于聚合函数的查询

具体实现步骤
1.导入jar包

2.创建JdbcTemplate对象
由于创建JdbcTemplate对象依赖于数据源DataSource,所以使用C3P0+匿名对象创建JdbcTemplate对象

JdbcTemplate template = new JdbcTemplate(new ComboPooledDataSource());

3.不同方法介绍

update();这个方法有许多重载

例如

  String sql = "update money set moneys=50 where id=1";
   int update = template.update(sql);

或者

 String sql = "update money set moneys=50 where id=?";
   int update = template.update(sql, 2);

queryForMap();方法

String sql2="SELECT * FROM money WHERE id = 2";
        Map<String, Object> map = template.queryForMap(sql2);
        // 遍历map集合
        Set<String> strings = map.keySet();
        for (String key : strings) {
            System.out.println( key+"="+map.get(key));
        }

queryForList();方法

        //3.3 queryForList方法
        String sql3="SELECT * FROM money ";
        List<Map<String, Object>> list = template.queryForList(sql3);
        // 遍历list集合
        // 获取迭代器对象
        ListIterator<Map<String, Object>> iterator = list.listIterator();
        while (iterator.hasNext()){
            Map<String, Object> map1 = iterator.next();
            System.out.println(map1);
        }

query();方法
使用该方法,需要传递两个参数sql语句和RowMapper接口

RowMapper接口:这个接口要求实现public <E> mapRow(ResultSet rs, int i)方法,我们可以选择自定义实现该接口,也可以选择已经实现好的类去实现

选择自定义实现该接口

       / 3.4 query():查询结果,将结果封装为JavaBean对象
        String sql4="SELECT * FROM money ";
        List<money> query = template.query(sql4, new RowMapper<money>() {
            @Override
            public money mapRow(ResultSet resultSet, int i) throws SQLException {
                money money = new money();
                / 取出但个数据
                int id = resultSet.getInt(1);
                String name = resultSet.getString(2);
                double moneys = resultSet.getDouble(3);

                / 封装到money对象中去
                money.setId(id);
                money.setName(name);
                money.setMoney(moneys);
                return money;
            }
        });

        System.out.println(query);

选择已经实现好的类去实现,直接调用BeanPropertyRowMapper方法,需要传递泛型,以及泛型的字节码文件;
如下

BeanPropertyRowMapper<money>(money.class));

 // 3.4 query():查询结果,将结果封装为JavaBean对象
        String sql5="SELECT * FROM money ";
        List<money> query1 = template.query(sql5, new BeanPropertyRowMapper<money>(money.class));
        System.out.println(query1);

queryForObject();方法

  //4. queryForObject:查询结果,将结果封装为对象
        String sql6 = "select count(ID) from money";
        Integer integer = template.queryForObject(sql6, Integer.class);
        System.out.println(integer);

全部代码

public class JdbcTemplates {
    public static void main(String[] args) {
        
        //2. 创建JdbcTemplate对象。
        JdbcTemplate template = new JdbcTemplate(new ComboPooledDataSource());
        String sql1 = "update money set moneys=50 where id=1";
       
        //3. 调用JdbcTemplate的方法来完成CRUD的操作
        //3.1 update方法
        int update = template.update(sql1); // 返回影响的行数
        System.out.println(update);
        //3.2 queryForMap方法
        String sql2="SELECT * FROM money WHERE id = 2";
        Map<String, Object> map = template.queryForMap(sql2);
        // 遍历map集合
        Set<String> strings = map.keySet();
        for (String key : strings) {
            System.out.println( key+"="+map.get(key));
        }
        
        //3.3 queryForList方法
        String sql3="SELECT * FROM money ";
        List<Map<String, Object>> list = template.queryForList(sql3);
        // 遍历list集合
        // 获取迭代器对象
        ListIterator<Map<String, Object>> iterator = list.listIterator();
        while (iterator.hasNext()){
            Map<String, Object> map1 = iterator.next();
            System.out.println(map1);
        }
        
        // 3.4 query():查询结果,将结果封装为JavaBean对象
        String sql4="SELECT * FROM money ";
        List<money> query = template.query(sql4, new RowMapper<money>() {
            @Override
            public money mapRow(ResultSet resultSet, int i) throws SQLException {
                money money = new money();
                // 取出但个数据
                int id = resultSet.getInt(1);
                String name = resultSet.getString(2);
                double moneys = resultSet.getDouble(3);

                // 封装到money对象中去
                money.setId(id);
                money.setName(name);
                money.setMoneys(moneys);
                return money;
            }
        });
        System.out.println(query);
        System.out.println("***********************");
        
        // 3.4 query():查询结果,将结果封装为JavaBean对象
        String sql5="SELECT * FROM money ";
        List<money> query1 = template.query(sql5, new BeanPropertyRowMapper<money>(money.class));
        System.out.println(query1);

        //4. queryForObject:查询结果,将结果封装为对象
        String sql6 = "select count(ID) from money";
        Integer integer = template.queryForObject(sql6, Integer.class);
        System.out.println(integer);
    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值