JDBC相关知识

一、接数据库

1. 步骤

//1.创建一个Driver实现类的对象
Driver driver = new com.mysql.jdbc.Driver();//注意抛异常

//2.准备 url 和 info
String url = "jdbc:mysql://localhost:3306/test";
//Oracle:"jdbc:oracle:thin:@localhost:1512:sid"
//SQLServer:"jdbc:microsoft:sqlserver//localhost:1433:DatabaseName=sid"
//MySql:"jdbc:mysql://localhost:3306/sid"

Properties info = new Properties();
info.put("user", "root");
info.put("password","1230");

//3.获取连接
Connection connection = driver.connect(url, info);//注意抛异常

 

2. 获取Driver的方式

  • 通过new新建  :Driver driver = new com.mysql.jdbc.Driver();
  • 通过反射创建类的实例  :Driver driver = (Driver)Class.forName(driverClass).newInstance();  //driverClass为全类名,即 com.mysql.jdbc.Driver
  • 通过DriverManager获取数据库连接(加载数据库驱动程序 即 注册驱动)  :DriverManager.registerDriver(Class.forName(driverClass).newInstance());   可简化为 Class.forName(driverClass); 可加载多个驱动
    //加载数据库驱动程序 (可加载多个驱动程序)(注册驱动)
    //DriverManager.registerDriver(Class.forName(driverClass).newInstance());
    Class.forName(driverClass);
    Class.forName(driverClass2);

    //通过getConnection方法获得连接
    //getConnection(String url, String user, String password) throws SQLException
    Connection connection = DriverManager.getConnection(jdbcUrl, user, password);

 

3. 提取方法来获得Connection

实现方式:把URL、账号、密码等信息放入 jdbc.properties 文件,通过反射获取

public class Utils {
    public Connection getConnection() throws Exception {
        String driverClass = null;
        String jdbcUrl = null;
        String user = null;
        String password = null;

        // 读取类路径下的jdbc.properties文件
        InputStream in = getClass().getClassLoader().getResourceAsStream("jdbc.properties");
        Properties properties = new Properties();
        properties.load(in);
        driverClass = properties.getProperty("driver");
        jdbcUrl = properties.getProperty("jdbcUrl");
        user = properties.getProperty("user");
        password = properties.getProperty("password");

/*        // 通过反射创建类的实例
        Driver driver = (Driver) Class.forName(driverClass).newInstance();

        Properties info = new Properties();
        info.put("user", user);
        info.put("password", password);
        Connection connection = driver.connect(jdbcUrl, info);*/
        
        Class.forName(driverClass);
        Connection connection = DriverManager.getConnection(jdbcUrl, user, password);

        return connection;
    }

}

 

二、数据库操作

前提:连接数据库的前提下,才能进行操作

1. 执行更新操作 (插入、修改、删除 )

//1.获得连接
Connection conn = driver.connect(url, info);
String sql="...";     //sql语句

//2.获取statement对象
Statement statement = conn.createStatement();

//3.执行
statement.executeUpdate(sql);

//4.关闭
statement.close();
conn.close();

 

2. 查询操作

//1.通过连接获得statement对象
Statement statement = conn.createStatement();
String sql = "...";

//2.执行
ResultSet rs = statement.executeQuery(sql);

//3.遍历输出
while(rs.next()){
     int id = rs.getInt(1);
     String name = rs.getString("name");
}

//4.关闭
rs.close();
statement.close();
conn.close();

 

3. 给SQL语句设置参数——PreparedStatement

父类:Statement

//1.通过连接获得preparedStatement对象
String sql = "insert into customers (name, email, birth) values(?,?,?)";
preparedStatement = conn.prepareStatement(sql);  //与无参的Statement对象不同,获得对象时就得传入sql语句,执行语句时不用再传入sql语句

//2.设置参数
preparedStatement.setString(1, "zhangsan");
preparedStatement.setString(2, "77777@qq.com");
preparedStatement.setDate(3, new Date(new java.util.Date().getTime()));

//3.执行
preparedStatement.executeUpdate();

//4.获得ResultSet
ResultSet rs = preparedStatement.getResultSet();

 

获取插入记录的主键值

实现方式:使用重载的preparedStatement(sql, flag)来生成preparedStatement对象

preparedStatement = conn.preparedStatement(sql, Statement.RETURN_GENERATED_KEYS);

//生成的结果集里只有一列,即主键编号 ResultSet rs
= preparedStatement.getGeneratedKeys(); if(rs.next()){
//获得主键 String generatedKey = rs.getObject(
1); }

 

4. 调用函数&存储过程——CallableStatement

父类:Statement

// 1. 通过 Connection 对象的 prepareCall()方法创建一个 CallableStatement 对象的实例。
//sql字符串指明如何调用存储过程:?=call name(param, ..., param) 或 callname(param, ..., param) String sql = "{?= call sum_salary(?, ?)}"; CallableStatement callableStatement = connection.prepareCall(sql); // 2. 通过 CallableStatement 对象的 reisterOutParameter() 方法注册 OUT 参数。 callableStatement.registerOutParameter(1, Types.NUMERIC); callableStatement.registerOutParameter(3, Types.NUMERIC); // 3. 通过 CallableStatement 对象的 setXxx() 方法设定 IN 或 IN OUT 参数。
//若想将参数默认值设为
null, 可以使用 setNull() 方法。 callableStatement.setInt(2, 80); // 4. 通过 CallableStatement 对象的 execute() 方法执行存储过程。 callableStatement.execute(); // 5. 如果所调用的是带返回参数的存储过程, 还需要通过 CallableStatement 对象的 getXxx() 方法获取其返回值。 double sumSalary = callableStatement.getDouble(1); long empCount = callableStatement.getLong(3);

 

三、JDBC元数据

1. ResultSetMetaData

方法签名:ResultSetMetaData getMetaData()       //通过 resultSet.getMetaData() 方法获得 ResultSetMetaData 对象

                  int getColumnCount()                            //查询结果集中的列数

                  String getColumnName(int colum)        //获取查询结果集中指定的列别名,从1开始

                  String getColumnLabel(int colum)         //获取查询结果集中指定的列别名,从1开始

    //在数据库中查询记录,转换成与表相对应的实体类对象
public <T> T get(Class<T> clazz, String sql, Object... args) throws Exception { T entity = null; Connection connection = null; PreparedStatement preparedStatement = null; ResultSet resultSet = null; //1.获得connection connection = getConnection(); //2.设置参数 preparedStatement = connection.prepareStatement(sql); for (int i = 0; i < args.length; i++) { preparedStatement.setObject(i + 1, args[i]); } //3.执行,并获得resultSet resultSet = preparedStatement.executeQuery(); //4.通过resultSet获得resultSetMetaData ResultSetMetaData resultSetMetaData = resultSet.getMetaData(); Map<String, Object> values = new HashMap<>(); if (resultSet.next()) { //5.把结果放入哈希表中 for (int i = 0; i < resultSetMetaData.getColumnCount(); i++) { String columnLabel = resultSetMetaData.getColumnLabel(i + 1); Object columnValue = resultSet.getObject(i + 1); values.put(columnLabel, columnValue); }
//6.为相应实体类对象设置属性值
if(values.size() > 0){ entity = clazz.newInstance(); for(Map.Entry<String, Object> entry: values.entrySet()){ String fieldName = entry.getKey(); Object value = entry.getValue(); BeanUtils.setProperty(entity, fieldName, value);//需要使用beanutils工具包 } } } return entity; }

 

2. DatabaseMetaData

方法签名:DatabaseMetaData getMetaData()      //通过 connection.getMetaData() 方法获得

作用:可以得到数据库本身的一些信息

DatabaseMetaData data = connection.getMetaData();
            //1. 得到数据库的版本号
int version = data.getDatabaseMajorVersion();
            
//2. 得到连接到数据库的用户名
String user = data.getUserName();
            
//3. 得到 MySQL 中有哪些数据库
ResultSet resultSet = data.getCatalogs();

 

四、处理Blob大对象数据

LOB:大对象,存储大量数据的一种数据类型。

BLOB:二进制大对象。

1. 读取 Blob 数据

 实现方式:使用 getBlob 方法读取到 Blob 对象

                   调用 Blob 的 getBinaryStream() 方法得到输入流。再使用 IO 操作即可。

...
resultSet = preparedStatement.executeQuery();
          
if(resultSet.next()){
    int id = resultSet.getInt(1);
    String name = resultSet.getString(2);
    String email = resultSet.getString(3);
                
    //1.获得Blob对象
    Blob picture = resultSet.getBlob(5);
    
    //2.获得输入流
    InputStream in = picture.getBinaryStream();
    
    //3.输出到flower.jpg
    OutputStream out = new FileOutputStream("flower.jpg");
    byte [] buffer = new byte[1024];
    int len = 0;
    while((len = in.read(buffer)) != -1){
        out.write(buffer, 0, len);
    }
                
    in.close();
    out.close();
}

 

2. 写入Blob数据

作用:实现将本地图片写入数据库

String sql = "INSERT INTO customers(name, email, birth, picture) VALUES(?,?,?,?)";
//通过连接 获得preparedStatement对象 preparedStatement
= connection.prepareStatement(sql);
//设置参数 preparedStatement.setString(
1, "ABCDE"); preparedStatement.setString(2, "abcde@atguigu.com"); preparedStatement.setDate(3, new Date(new java.util.Date().getTime()));
//1.创建输入流 InputStream inputStream
= new FileInputStream("Hydrangeas.jpg");
//2.设置Blob参数 preparedStatement.setBlob(
4, inputStream); preparedStatement.executeUpdate();

 

五、事务

1. 方法

//取消默认提交后才能进行事务

connection.setAutoCommit(false);  //取消默认提交并开始事务

//提交事务

connection.commit();

//回滚

 connection.rollback();

 

2. 数据库的隔离级别

  • READ UNCOMMITTED  :读未提交数据,允许事务读取未被其他事务提交的变更,脏读、不可重复读、幻读都会出现。
  • READ COMMITTED       :读已提交数据,只允许事务读取已经被其他事务提交的变更。可避免脏读,不可重复读和幻读仍然存在。
  • REPEATABLE READ      :可重复读,确保事务可以多次从一个字段中读取相同的值,在这个事务持续期间,禁止其他事务对这个字段进行更新。可避免脏读和不可重复读,幻读问题仍然存在。
  • SERIALIZABLE          :串行化,确保事务可以从一个表中读取相同的行,在这个事务持续期间,禁止其他事务对该表执行操作,所有并发问题都可避免,但性能低下。

通过Connection的setTransactionIsolation来设置事务的隔离级别。

例:connection.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);

注:mysql的默认隔离级别是REPEATABLE READ。

 

六、批量处理

方法签名:preparedStatement.addBatch();         //加入Batch

                  preparedStatement.executeBatch();  //批量处理

                  preparedStatement.clearBatch();      //清除当前的积累量

//批量插入十万条数据
sql = "INSERT INTO customers VALUES(?,?,?)";
preparedStatement = connection.prepareStatement(sql);
Date date = new Date(new java.util.Date().getTime());
for
(int i = 0; i < 100000; i++){ preparedStatement.setInt(1, i + 1); preparedStatement.setString(2, "name_" + i); preparedStatement.setDate(3, date); //"积攒" SQL preparedStatement.addBatch();  //Statement写法:statement.addBatch(String sql); //当 "积攒" 到一定程度, 就统一的执行一次. 并且清空先前 "积攒" 的 SQL if((i + 1) % 300 == 0){ preparedStatement.executeBatch(); preparedStatement.clearBatch(); } } //若总条数不是批量数值的整数倍, 则还需要再额外的执行一次. if(100000 % 300 != 0){ preparedStatement.executeBatch(); preparedStatement.clearBatch(); }

 

七、数据库连接池

必要性

传统连接使用DriverManager获取,再验证用户名密码,执行完毕再断开连接。数据库的连接资源没有得到很好的重复利用。

数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个。

数据库连接池是通过实现DataSource接口来实现的。

 

1. DBCP

需要2 个jar 包:

  • dbcp
  • pool

注:dbcp依赖于 pool

final BasicDataSource dataSource = new BasicDataSource();
        
//1. 为数据源实例指定必须的属性
dataSource.setUsername("root");
dataSource.setPassword("1230");
dataSource.setUrl("jdbc:mysql://localhost:3306/test");
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        
//2. 指定数据源的一些可选的属性
//1). 指定数据库连接池中初始化连接数的个数
dataSource.setInitialSize(5);
        
//2). 指定最大的连接数: 同一时刻可以同时向数据库申请的连接数
dataSource.setMaxActive(5);
        
//3). 指定小连接数: 在数据库连接池中保存的最少的空闲连接的数量 
dataSource.setMinIdle(2);
        
//4).等待数据库连接池分配连接的最长时间. 单位为毫秒. 超出该时间将抛出异常. 
dataSource.setMaxWait(1000 * 5);
        
//3. 从数据源中获取数据库连接
Connection connection = dataSource.getConnection();

 

通过DataSourceFactory创建连接池(DataSource),这样可以直接从配置文件中获取信息

//从Properties文件里获得数据
Properties properties = new Properties(); InputStream inStream = DBCPTest.class.getClassLoader().getResourceAsStream("dbcp.properties"); properties.load(inStream);
//使用工厂创建连接池 DataSource dataSource
= BasicDataSourceFactory.createDataSource(properties);

 

 

2. C3P0

@Test
public void testC3P0() throws Exception{
    ComboPooledDataSource cpds = new ComboPooledDataSource();
    cpds.setDriverClass( "com.mysql.jdbc.Driver" ); //loads the jdbc driver            
    cpds.setJdbcUrl( "jdbc:mysql://localhost:3306/test" );
    cpds.setUser("root");
    cpds.setPassword("1230");
        
    System.out.println(cpds.getConnection()); 
}

 

通过加载配置文件来获得信息

DataSource dataSource = new ComboPooledDataSource("helloc3p0");

dataSource.getConnection();

<!-- 配置文件helloc3p0.xml -->
<?
xml version="1.0" encoding="UTF-8"?> <c3p0-config> <named-config name="helloc3p0"> <!-- 指定连接数据源的基本属性 --> <property name="user">root</property> <property name="password">1230</property> <property name="driverClass">com.mysql.jdbc.Driver</property> <property name="jdbcUrl">jdbc:mysql:///atguigu</property> <!-- 若数据库中连接数不足时, 一次向数据库服务器申请多少个连接 --> <property name="acquireIncrement">5</property> <!-- 初始化数据库连接池时连接的数量 --> <property name="initialPoolSize">5</property> <!-- 数据库连接池中的最小的数据库连接数 --> <property name="minPoolSize">5</property> <!-- 数据库连接池中的最大的数据库连接数 --> <property name="maxPoolSize">10</property> <!-- C3P0 数据库连接池可以维护的 Statement 的个数 --> <property name="maxStatements">20</property> <!-- 每个连接同时可以使用的 Statement 对象的个数 --> <property name="maxStatementsPerConnection">5</property> </named-config> </c3p0-config>

 

八、DBUtils

注:dbutils只是对底层的封装

       QueryRunner可能会有线程安全问题,逐一不要创建多个实例

1. update方法

//1.创建QueryRunner实现类
QueryRunner queryRunner = new QueryRunner(); String sql = "DELETE FROM customers WHERE id IN (?,?)";

//2.使用update方法 queryRunner.update(connection, sql,
12, 13);

 

2. query方法

方法签名:queryRunner.query(Connection connection, String sql, ResultSetHandler rs);

使用:在实现ResultSetHandler接口的类的handle方法中编写对结果集的处理方法。

Object object = queryRunner.query(conn, sql, new ResultSetHandler() {
    @Override
    public Object handle(ResultSet rs) throws SQLException {
        List<Customer> customers = new ArrayList<>();

        while (rs.next()) {
            int id = rs.getInt(1);
            String name = rs.getString(2);
            String email = rs.getString(3);
            Date birth = rs.getDate(4);

            Customer customer = new Customer(id, name, email, birth);
            customers.add(customer);
        }
//也是query方法的返回值
return customers; } });

 

以下Handler类实现了ResultSetHandler接口

1)BeanHandler

功能:返回查询的第一条记录对应的那个实体类对象

原理:BeanHandler类的handle方法中通过反射创建传入的Class类对应的类的对象,并用set方法为该对象设置属性,然后返回该对象。

注意:查询语句中的列名要和实体类的属性相对应,若不对应则需要使用别名   如下name:

String sql = "SELECT id, name customerName, email, birth FROM customers WHERE id >= ?";

// 1. 创建 QueryRunner 对象
QueryRunner queryRunner = new QueryRunner();

// 2. 调用query方法 Object object = queryRunner.query(conn, sql, new BeanHandler<>(Customer.class),5);

 

2)BeanListHandler

作用:可以通过查询记录获得实体类对象集合

           若可以查询到记录,则返回传入的Class对象对应的对象集合。

String sql = "SELECT id, name customerName, email, birth FROM customers";
QueryRunner queryRunner = new QueryRunner();

// 返回List<Customer> 对象 Object object
= queryRunner.query(conn, sql, new BeanListHandler<>(Customer.class));

 

3)MapHandler

作用:返回查询的第一条记录对应的Map对象  键:列名(非别名);值:列的值

QueryRunner queryRunner = new QueryRunner();
String sql = "SELECT id, name customerName, email, birth FROM customers WHERE id = ?";

Map<String, Object> map = queryRunner.query(connection, sql, new MapHandler(), 4);

 

4)MapListHandler

作用:返回查询的所有记录对应的Map对象的集合

QueryRunner queryRunner = new QueryRunner();
String sql = "SELECT id, name, email, birth FROM customers";

List<Map<String, Object>> mapList = queryRunner.query(connection, sql, new MapListHandler());

 

5)ScalarHandler

作用:返回结果集中的第一列对应的数据

QueryRunner queryRunner = new QueryRunner();
String sql = "SELECT name FROM customers WHERE id = ?";

Object count = queryRunner.query(connection, sql, new ScalarHandler(), 6);

 

3. QueryLoader

作用:可以用来加载存放着 SQL 语句的资源文件。

          使用该类可以把 SQL 语句外置化到一个资源文件中. 以提供更好的解耦。

// / 代表类路径的根目录
Map<String, String> sqls = QueryLoader.instance().load("/sql.properties");
        
String updateSql = sqls.get("UPDATE_CUSTOMER");

 

posted @ 2017-07-13 00:08  CComma  阅读(335)  评论(0编辑  收藏  举报