栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 前沿技术 > 大数据

Java中连接数据库(JDBC)

大数据 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Java中连接数据库(JDBC)

目录
  • 一、 初识MySQL
    • 1.1 数据库分类
      • 1.1.1 MySQL简介
  • 二,基本sql语句
  • 三、 JDBC
    • 3.1数据库驱动
    • 3.2 Java中建立数据库连接 以8.0版本为例
      • 3.2.1 加载驱动 Class.forName(com.mysql.cj.jdbc.Driver)
      • 3.2.2 DriverManager.getConnection() 连接数据库
      • 3.2.3 获得用于执行数据库操作的statement接口对象connectionName.createStatement();
      • 3.2.4 用statement接口对象操作数据库啦! statementName.execute...
          • 例一:查询并接收结果集,循环打印结果集
          • 例二:更新数据库——插入数据举例
          • 补充一:JDBC的批量操作
          • 补充二:PreparedStatement对象
      • 3.2.5 释放资源
    • 3.3 提取工具类
    • 3.4 关于statement资源关闭的问题
    • 3.6 SQL注入问题
    • 3.7 事务
      • 3.7.1隔离性产生的问题
        • 脏读
        • 不可重复读
        • 虚读(幻读):
      • 3.7.2 代码实现
  • 四、 数据库连接池
    • 4.1 池化技术
    • 4.2 数据库连接池
      • 4.2.1使用传统的DBCP
          • 配置文件:dbcpconfig.properties
          • 封装的工具类JdbcUtilsDBCP
      • 4.2.2 C3P0
        • 配置文件:c3p0-config.xml
          • 封装的工具类
    • 4.3 结论
  • 五、在ideal内连接数据库

一、 初识MySQL 1.1 数据库分类

关系型数据库:(SQL)

  • MySQL,Oracle,Sql Server,DB2,SQLlite
  • 通过表和表之间,行和列之间的关系进行数据的存储。

非关系型数据库:(NO SQL) not only sql

  • Redis,MongoDB
  • 对象存储,通过对象自身的属性来决定

DBMS(数据库管理系统)

  • 数据库管理软件,科学有效地管理数据。维护和获取数据
  • MySQL:关系型数据库管理系统(RDBMS)
1.1.1 MySQL简介
  • 关系型数据库管理系统
  • Oracle公司
  • 开源数据库软件
  • 体积小,速度快,下哦那个提拥有成本低
  • 官网:https://www.mysql.com
二,基本sql语句 三、 JDBC

JDBC(Java Data base Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。是Java访问数据库的标准规范
JDBC提供了一种基准,据此可以构建更高级的工具和接口,使数据库开发人员能够编写数据库应用程序。
JDBC需要连接驱动,驱动是两个设备要进行通信,满足一定通信数据格式,数据格式由设备提供商规定,设备提供商为设备提供驱动软件,通过软件可以与该设备进行通信。

3.1数据库驱动

sun公司伪了简化开发人员(在数据库上统一)地操作,提供了一个(Java操作数据库)的规范,俗称JDBC,具体的规范的实施有专门的厂商去做。对于开发人员只需要掌握JDBC的接口操作即可

3.2 Java中建立数据库连接 以8.0版本为例

①写代码前必须安装对应驱动(驱动版本需要与mysql版本一致),后期可以使用maven配置
②将对应版本的驱动加入到使用数据库的moudle中(重要:如果位置不对后期会报错ClassNotFound)
③写代码

3.2.1 加载驱动 Class.forName(com.mysql.cj.jdbc.Driver)
		Class.forName(com.mysql.cj.jdbc.Driver)

该行代码的路径来源:

如果mysql和mysql驱动的版本不是8.0路径会不一样,那么此行代码也会不一样。

3.2.2 DriverManager.getConnection() 连接数据库
  • 参数一:url —— 要连接的数据库地址 固定写法不可改变

组成:协议://主机地址:端口号(默认3306)/数据库名?参数一&参数二&参数三&参数四&...

  • 参数二:username —— 要连接的用户名
  • 参数三:password —— 要连接的密码
        //2、用户信息和url
        String url = "jdbc:mysql://localhost:3306/jdbc_Study?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false";
        String username = "root";
        String pwd = "123456";

        //3、连接数据库
        //connection代表一个数据库连接
        Connection connection = DriverManager.getConnection(url,username,pwd);

3.2.3 获得用于执行数据库操作的statement接口对象connectionName.createStatement();

statement:执行类

作用:用于向数据库发送方SQL数据,完成堆数据库的操作

  • 数据库设置自动提交setAutoCommit(true)
  • 事务提交 :commit();
  • 数据回滚:rollback();
    Java中万事万物皆对象,自然操作数据库也得通过对象来实现;
        Statement statement = connection.createStatement();
3.2.4 用statement接口对象操作数据库啦! statementName.execute…

execute是所有操作的前缀,需要说明操作这里就用什么方法

  • executeUpdate(String sql):所有的对数据库进行的更新操作,如增、删、改、查、插等都使用此方法。返回int型数字,代表修改的行数。
  • executeQuery(String sql):数据库的索引查询操作,最后会返回一个类型为ResultSet的结果集。只有查询操作有结果集
例一:查询并接收结果集,循环打印结果集
        //5.1 创建SQL语句对象
        String SQL = "select * from student";
        //5.2 执行sql并接收结果集
        ResultSet resultSet1 = statement.executeQuery(SQL);
        //int i = statement.executeUpdate("insert into student(id,sname,age) values(1003,"韩桐",20);");
        //System.out.print(i);
        //注意此处如果上述被注释的语句执行则resultSet1已经被释放
        while (resultSet1.next()){
            System.out.println("id=" + resultSet1.getObject("id")+"StudentName="+resultSet1.getString("sname"));
        }

输出:

补充操作:

	resultSet.beforeFirst();//移动到最前面
	resultSet.afterLast();//移动到最后面
	resultSet.next();//移动到下一个数据
	resultSet.previous();//移动到前一行
	resuleSet.absolute(int row);// 移动到指定行
例二:更新数据库——插入数据举例
        int i = statement.executeUpdate("insert into student(id,sname,age) values(1004,"韩筠",20);");
        System.out.print(i);
补充一:JDBC的批量操作

addBatch(String):添加需要批量处理的SQL语句或是参数;

executeBatch();执行批量处理语句;

clearBatch();清除批量打包

补充二:PreparedStatement对象

与Statement对象的区别:PreparedStatement可以防止SQL注入且效率更高
步骤:编写sql->预编译->传递参数->执行

            //prepareStatement()的写法
            //使用?占位符代替参数
            String sql = "insert into student(id,sname,age) values (?,?,?);";
            //预编译SQL,先写sql,然后不执行
            statement = connection.prepareStatement(sql);

            //手动给参数赋值setObject(参数下标(参数位置),参数的值)
            statement.setInt(1, 1008);
            statement.setString(2, "刘芊");
            statement.setInt(3, 20);

            //执行executeUpdate(),无参数
            int i = statement.executeUpdate();
3.2.5 释放资源

释放资源是必须做的,这些源是非常占用内存的,且为了避免因为异常资源无法释放,资源的释放应该使用try-catch-finally来处理

       try{
       ...
       }catch{
       ...
       }finally {
        //6、释放连接
            if (resultSet1 != null){

                try {
                    resultSet1.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (statement != null) {
                try {
                    statement.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (connection != null) {
                try {
                    connection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
3.3 提取工具类

以上的代码每次协议去对数据库执行操作时都要重新写一变代码,会造成代码冗余,且代码的可扩展性不强

同时为了体现Java特有的封装性,我们应该对上述代码提取工具类。且连接数据库的信息放在代码里面会造成强耦合,所以应该把jdbc的配置放在文件db.properties里

db.properties:

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbc_study?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false
user=root
password=123456

新建工具类JdbcUtility.class:

import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;


public class JdbcUtility {
    //通过反射得到工具类的加载器获得相应资源

    private static String driver= null;
    private static String url= null;
    private static String name= null;
    private static String pwd= null;
    private String sql;
    private Statement statement;


    public JdbcUtility(String sql) {
        this.sql = sql;
    }

    static {
        try{
            InputStream in = JdbcUtility.class.getClassLoader().getResourceAsStream("db.properties");
            Properties properties = new Properties();
            properties.load(in);

            driver = properties.getProperty("driver");
            url = properties.getProperty("url");
            name = properties.getProperty("user");
            pwd = properties.getProperty("password");

            //1、驱动只用加载一次
            Class.forName(driver);



        }catch (IOException | ClassNotFoundException e){
            e.printStackTrace();
        }
    }

    //2、获取连接
    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection(url, name, pwd);
    }



    //3、释放资源
    public void close(Connection connection,Statement statement,ResultSet resultSet){

        if (connection != null) {
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (statement!=null) {
            try {
                statement.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (resultSet != null) {
            try {
                resultSet.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

}

新建工具类的测试类JDBCUtilityTest.class:
利用Test类测试

{

    @Test
    public void tes1() {
        Connection connection = null;
        Statement statement = null;
        ResultSet resultSet =null;
        try {
            //连接数据库
            connection = JdbcUtility.getConnection();

            statement = connection.createStatement();

            resultSet = statement.executeQuery("select * from student");
            while (resultSet.next()){
                System.out.println( "id=" + resultSet.getInt("id"));
                System.out.println( "id=" + resultSet.getString("sname"));
                System.out.println( "id=" + resultSet.getInt("age"));
                System.out.println( "===================");
            }
            int i = statement.executeUpdate("insert into student(id,sname,age) values (0000,'张三',23);");
            System.out.println(i+"条数据被添加");
            int j = statement.executeUpdate("delete from student where id = 0000");
            System.out.println(j+"条数据被删除");

        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
        
            if (connection != null) {
                try {
                    connection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }

                if (resultSet != null) {
                    try {
                        resultSet.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }

                if (statement != null) {
                    try {
                        statement.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }


}

数据库student中存数据:

输出:

id=1001
id=李雷
id=20
===================
id=1002
id=韩梅梅
id=20
===================
id=1003
id=韩桐
id=20
===================
id=1004
id=韩筠
id=20
===================
1条数据被添加
1条数据被删除
3.4 关于statement资源关闭的问题

在对使用statement对象进行操作时,对查询操作executeQuery得到的结果集的遍历操作,如果在更新操作之后再进型,即将下图中红圈与蓝圈的代码位置对调
对调之后,执行代码会报资源已经关闭不能使用的异常:java.sql.SQLException: Operation not allowed after ResultSet closed

查看官方文档的解释说明:
重点:

当ResultSet对应的Statemet对象被close()关闭,重新执行,或用于从多个结果序列中检索下一个结果时,Result对象将自动关闭.

在上述代码中,在resultSet的statement对象在执行查询操作后,再次被重新执行,这时候resultSet就已经被关闭,而我并没用数组变量将得到的数据存储起来,导致在一系列操作完成之后再想去遍历查询出来的数据时,相关资源已经被回收。

此时resultSet都还未close;

当statement对象再次被使用时resultSet资源关闭


如图在更新操作前直接调用close关闭resultSet会得到相同的报错信息,再次验证。

3.6 SQL注入问题

目前,所有的SQL都会存在SQL注入问题,会采用其他的statement对象的创建方式
sql注入本质是SQL的拼接
sql存在漏洞,会导致网站数据泄露

3.7 事务

要么都成功,要么都失败

ACID原则

原子性:要么全部完成,要么都不完成
一致性:总数不变
隔离性:多个进程互不干扰
持久性:一旦提交不可回滚

3.7.1隔离性产生的问题 脏读

一个事务读取了另一个事务没有提交的数据

不可重复读

在同一个事务内,重复读取表中的数据,表数据发生改变

虚读(幻读):

在一个事务内,读取到了别人插入的数据导致前后读出来的结果不一致。

3.7.2 代码实现
  • 开启事务:``conn.setAutoCommit(false);关闭自动提交开启事务
  • 一组业务执行完毕,提交事务
  • 可以在catch语句中显示执行回滚语句conn.rollback(),但是程序一旦失败就会默认回滚

四、 数据库连接池 4.1 池化技术

操作数据库的过程:数据库连接----执行完毕----释放 。释放的过程是是十分浪费资源的
为啥对象不是想要就能New出来一个?主要是成本,有些对象创建的代价比较大,比如线程、tcp连接、数据库连接等对象。对于这些创建耗时较长,或者资源占用较大(占据操作系统资源。

对于线程,内存,数据库的连接对象等等,这些都是资源,程序中当你创建一个线程或者在堆上申请一块内存时,都涉及到很多系统调用,也是非常消耗CPU的,如果你的程序需要很多类似的工作线程或者需要频繁的申请释放小块内存,如果没有在这方面进行优化,那很有可能这部分代码将会成为影响你整个程序性能的瓶颈。

而对于这些对象我们会反复的重复性的使用,如果能有一个“池子”存放这些资源对象就好了,我用的时候从中取出一个即可,“池子”自身去维护这些资源对象。

于是我们总结一些,首先这些资源对象进行抽象总结

对象有如下共同特点:

1.对象创建时间长;2.对象创建需要大量资源;3.对象创建后可被重复使用。

于是设计的管理资源池应具备如下功能:

1.租用资源对象;2.归还资源对象;3.清除过期资源对象。

通过对连接或线程等资源的复用,并对复用的数量、时间等进行控制,使系统的性能和资源消耗达到最优的状态;

池化技术能够减少资源对象的创建次数,提高程序的性能,特别是在高并发下这种提高更加明显。

实际项目中,使用池化技术来解决程序性能的瓶颈。

池化技术 - 简单点来说,就是提前保存大量的资源,以备不时之需。

上述池化技术引用自文章: “池化”技术:对象,只要你想要,想要就能要!.
池化技术:准备一些预先的资源,过来就直接连接准备好的资源

4.2 数据库连接池

以下所有的jdbcp配置都是对mysql8.0版本的

DBCP

  • C3P0:是一个开放源代码的JDBC连接池,它在lib目录中与Hibernate 一起发布,包括了实现jdbc3和jdbc2扩展规范说明的Connection 和Statement 池的DataSources 对象
  • Druid:阿里巴巴

要适用一个数据库连接池,需要导入相应的jar包,一般jar可以在官网下载二进制文件,也可以在配置号maven后直接在ideal内下载(此处我还没有学maven)

4.2.1使用传统的DBCP

需要的jar包:commons-dbcp,commons-pool,可能需要commons-logging-1.2.jar,版本尽量一致

配置文件:dbcpconfig.properties
#连接设置 这里的名字是DBCP数据源中定义的不可改改
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbc_study?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=true
username=root
password=123456

#初始化连接
initialSize=10

#最大连接数
maxActive=50

#最大空闲连接
maxIdIe=20

#最小空闲连接
minIdIe=20

#超时等待时间ms
maxWait=60000

#JDBC驱动建立连接时附带的连接属性,属性的格式必须为:[属性名=property]
#注意”user“与”password“两个属性会被明确地传递,因此不需要包含他们
connectionProperties=useUnicode=true;characterEncoding=UTF8

#指定由连接池所创建的自动提交(auto-commit)状态:
defaultAutoCommit=true;

#driver default 指定又连接池所创建的连接的只读(read-only)状态
#如果没有设置该值,则”setReadOnly“方法将不被调用。(某些驱动并不支持只读模式,如:Informix)
defaultReadonly=

#driver default 指定由连接池所创建的连接事务的级别(TransactionIsolation)
#可用值为下列之一:(详情可见javadoc)NONE,READ_UNCOMMITTED,READ_COMMITTED,REPEATABLE_READ,SERIALIZABLE
defaultTransactionIsolation=READ_UNCOMMITTED

使用了这些数据库连接池之后,我们在项目开发中就不需要编写连接数据库的代码了

封装的工具类JdbcUtilsDBCP

与我们自定义的工具类相比DBCP数据库连接池使得我们不用再手动单独创建connection对象和加载驱动。

    private static DataSource dataSource;
    static {
        try{
            InputStream in = JdbcUtilsDBCP.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");
            Properties properties = new Properties();
            properties.load(in);

            //创建数据源 工厂模式--->创建对象
            dataSource = BasicDataSourceFactory.createDataSource(properties);

        }catch (Exception e){
            e.printStackTrace();
        }
    }

    //2、获取连接
    public static Connection getConnection() throws SQLException {
        //从数据源中获取连接
        return dataSource.getConnection();
    }
    //3、释放资源
    public static void close(Connection connection, Statement statement, ResultSet resultSet){
    //按顺序关闭ResultSet,statement、connection
    }

测试类:

    @Test
    public void testDBCP() {

        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        try {
            String sql = "select * from student";
            connection = JdbcUtilsDBCP.getConnection();
            preparedStatement = connection.prepareStatement(sql);
            resultSet = preparedStatement.executeQuery();

            while (resultSet.next()) {
                System.out.print("id=" + resultSet.getInt("id") + "t");
                System.out.print("name=" + resultSet.getString("sname") + "t");
                System.out.println("age=" + resultSet.getInt("age"));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JdbcUtilsDBCP.close(connection, preparedStatement, resultSet);
        }


    }
4.2.2 C3P0

需要的jar包:commons-dbcp,commons-pool-java

配置文件:c3p0-config.xml





    
        com.mysql.cj.jdbc.Driver
        jdbc:mysql://localhost:3306/jdbc_studyuseUnicode=true&characterEncoding=utf8;serverTimezone=GMT%2B8;useSSL=true
        root
        123456

        5
        10
        5
        20
    

    
    
        com.mysql.cj.jdbc.Driver
        jdbc:mysql://localhost:3306/jdbc_study?
        root
        123456

        5
        10
        5
        20

    



封装的工具类

ComboPooledDataSource
与DBCP相比,c3p0的工具类进一步简化只需要再静态代码中,实例化一个ComboPooledDataSource对象,此对象是为了用来获取连接且ComboPooledDataSource类也是继承于DataSource接口类的。

    private static ComboPooledDataSource dataSource;

    static {
        try{
            //配置文件写法
            dataSource = new ComboPooledDataSource("MySQL");

            //代码版配置不建议
            //dataSource = new ComboPooledDataSource();
            //dataSource.setDriverClass();
            //dataSource.setUser();
            //dataSource.setPassword();
            //dataSource.setJdbcUrl();
            //
            //dataSource.setMaxPoolSize();
            //dataSource.setMinPoolSize();


        }catch (Exception e){
            e.printStackTrace();
        }
    }

    //获取连接,固定写法
    public static Connection getConnection() throws SQLException {
        return dataSource.getConnection();
    }

    //释放连接
    public static void release(Connection conn, Statement statement, ResultSet resultSet){
        if(resultSet != null){
            try {
                resultSet.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

        if(statement != null){
            try {
                statement.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

        if(resultSet != null){
            try {
                resultSet.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
4.3 结论

无论使用哪一个数据源,本质上都是一样的,共同点:都必须实现DataSource接口,
并且提供getConnection()方法,最后提供释放资源的realase方法。
拓展:了解apache,安装maven。

五、在ideal内连接数据库

第一步选择数据库

第二步连接数据库

第三步导入需要的表

查看表结构,双击打开

更新数据,双击修改数据并提交

写SQL语句

切换数据库

数据库连接失败的处理方式

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/278277.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号