JDBC简略笔记(一)

236 阅读5分钟

最近事情多,JavaWeb耽误了很久,看到后面就会把前面给忘了。我是跟着视频学习的,视频讲得很全,但是有令人崩溃的地方,那就是把内容讲得比较全,而实际项目用到的内容却仅仅是一小部分。而自己是个小白,自然会在一些不那么重要的地方花费太多的尽力,对此深恶痛绝。想来就需要进行对自己学习的东西进行一些总结,以防止我遗忘!

本篇笔记参考:尚硅谷JDBC课程+另一位博主的笔记

1、JDBC的基础理解

JDBC(Java Database Connectivity)是一个独立于特定数据库管理系统、通用的SQL数据库存取和操作的公共接口(一组API)。即意味着学好那几个接口的实现就行! 使用这套API可以实现对具体数据库的操作(获取连接、关闭连接、DML、DDL、DCL

image.png

2、环境介绍

  • 在数据库中有工程对应的库,并且创建了表
  • 在Java工程中的 lib 中导入 jdbc 的jar包

3、详细步骤

3.1、创建数据库配置文件

创建好 jdbc.properties 配置文件,并放在resource文件夹中

user=root
password=root
url=jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true
driverClass=com.mysql.jdbc.Driver

另外,常见的数据库URL地址的写法:

  • Oracle:jdbc:oracle:thin:@localhost:1521:数据库名
  • SqlServer:jdbc:microsoft:sqlserver://localhost:1433; DatabaseName=数据库名
  • MySql:jdbc:mysql://localhost:3306/数据库名

3.2、读取数据库配置文件信息

        InputStream inputStream = ConnectionTest.class.getClassLoader().getResourceAsStream("jdbc.properties");
        Properties prop = new Properties();
        prop.load(inputStream);
        String url = prop.getProperty("url");
        String user = prop.getProperty("user");
        String password = prop.getProperty("password");
        String driverClass = prop.getProperty("driverClass");

3.3、加载驱动与获取连接

       //加载驱动
        Class.forName(driverClass);

        //获取连接
        Connection connection = DriverManager.getConnection(url, user, password);

完整的获取连接方法:

    public void testConnection5() throws Exception{
        //1.读取配置文件中的基本信息,通过类加载器getClassLoader
        InputStream inputStream = ConnectionTest.class.getClassLoader().getResourceAsStream("jdbc.properties");

        Properties prop = new Properties();
        prop.load(inputStream);

        String url = prop.getProperty("url");
        String user = prop.getProperty("user");
        String password = prop.getProperty("password");
        String driverClass = prop.getProperty("driverClass");

        //加载驱动
        Class.forName(driverClass);

        //获取连接
        Connection connection = DriverManager.getConnection(url, user, password);

        System.out.println(connection);
    }

3.4 执行SQL语句,实现数据库CRUD操作

事前准备

在util文件夹中创建JDBCUtils类:

/**
 * 操作数据库的工具类
 */
public class JDBCUtils {
    /**
     * 获取数据库的连接
     */
    public static Connection getConnection() throws Exception {
        //1.读取配置文件的4个基本信息
        InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties");
        Properties pros = new Properties();
        pros.load(is);

        String user = pros.getProperty("user");
        String password = pros.getProperty("password");
        String url = pros.getProperty("url");
        String driverClass = pros.getProperty("driverClass");

        //2.加载驱动
        Class.forName(driverClass);

        //3.获取连接
        Connection connection = DriverManager.getConnection(url, user, password);

        return connection;
    }

    /**
     * 关闭连接和statement的操作
     * @param connection
     * @param ps
     */
    public static void closeResource(Connection connection, Statement ps) {
        try {
            if (ps != null) {
                ps.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        try {
            if (connection != null) {
                connection.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * 关闭资源的操作,用到了方法的重载
     * @param connection
     * @param ps
     * @param rs
     */
    public static void closeResource(Connection connection, Statement ps,ResultSet rs){
        try {
            if (ps != null) {
                ps.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        try {
            if (connection != null) {
                connection.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        try {
            if (rs != null) {
                rs.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

3.4.1 Statement不推荐

一般不直接用这个类,主要两个弊端:

  1. 拼写sql语句涉及字符串的拼接,很麻烦。
  2. sql注入问题,极度危险。

3.4.2 PreparedSatement推荐使用

PreparedSatement 是 statement的子接口。可以解决Statement的sql注入问题和拼串问题。这一部分没啥细说的,就是一些模板。

  • 通用的增删改操作
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]);  //这里有一个细节那就是从1开始的!
    }
    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;
}
public <T> List<T> getForList(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();
    //创建集合对象
    ArrayList<T> list = new ArrayList<T>();
    while (rs.next()) {
        T t = clazz.newInstance();
        // 处理结果集一行数据中的每一个列:给t对象指定的属性赋值
        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);
        }
        list.add(t);
    }		
    return list;
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        JDBCUtils.closeResource(conn, ps, rs);
    }
    return null;
}

3.5 操作Blob类型的变量

概括来说也就是写入操作和读取操作。 写入操作的方法:setBlob(InputStream is); 读取操作的方法: Blob blob = getBlob(int index); InputStream is = blob.getBinaryStream();

具体的方法:

//insert
//向数据表customers中插入Blob类型的字段
@Test
public void testInsert() throws Exception{
    Connection conn = JDBCUtils.getConnection();
    String sql = "insert into customers(name,email,birth,photo)values(?,?,?,?)";	
    PreparedStatement ps = conn.prepareStatement(sql);
    ps.setObject(1,"周末");
    ps.setObject(2, "yuan@qq.com");
    ps.setObject(3,"1992-09-08");
    FileInputStream is = new FileInputStream(new File("girl.jpg"));
    ps.setBlob(4, is);	
    ps.execute();	
    JDBCUtils.closeResource(conn, ps);		
}
//查询数据表customers中Blob类型的字段
@Test
public void testQuery(){
    Connection conn = null;
    PreparedStatement ps = null;
    InputStream is = null;
    FileOutputStream fos = null;
    ResultSet rs = null;
    try {
        conn = JDBCUtils.getConnection();
        String sql = "select id,name,email,birth,photo from customers where id = ?";
        ps = conn.prepareStatement(sql);
        ps.setInt(1, 21);
        rs = ps.executeQuery();
        if(rs.next()){
            int id = rs.getInt("id");
            String name = rs.getString("name");
            String email = rs.getString("email");
            Date birth = rs.getDate("birth");				
            Customer cust = new Customer(id, name, email, birth);				
            //将Blob类型的字段下载下来,以文件的方式保存在本地
            Blob photo = rs.getBlob("photo");
            is = photo.getBinaryStream();
            fos = new FileOutputStream("zhangyuhao.jpg");
            byte[] buffer = new byte[1024];
            int len;
            while((len = is.read(buffer)) != -1){
                fos.write(buffer, 0, len);
            }				
        }
    } catch (Exception e) {
        e.printStackTrace();
    }finally{			
        try {
            if(is != null)
                is.close();
        } catch (IOException e) {
            e.printStackTrace();
        }			
        try {
            if(fos != null)
            fos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }			
            JDBCUtils.closeResource(conn, ps, rs);
    }		
}

3.6 高效的批量插入

  • 设置不允许自动提交数据
  • .addBatch 先"攒",再一批批执行
@Test
public void testInsert3() {
    Connection conn = null;
    PreparedStatement ps = null;
    try {
        conn = JDBCUtils.getConnection();

        //设置不允许自动提交数据
        conn.setAutoCommit(false);
		
        String sql = "insert into goods(name)values(?)";
        ps = conn.prepareStatement(sql);
        for(int i = 1;i <= 1000000;i++){
            ps.setObject(1, "name_" + i);
				
            //1."攒"sql
            ps.addBatch();
				
            if(i % 500 == 0){
            //2.执行batch
            ps.executeBatch();
					
            //3.清空batch
            ps.clearBatch();
            }				
        }
			
        //提交数据
        conn.commit();	

    } catch (Exception e) {								
        e.printStackTrace();
    }finally{
        JDBCUtils.closeResource(conn, ps);			
    }		
}

需要注意的点: mysql服务器默认是关闭批处理的,我们需要通过一个参数,让mysql开启批处理的支持。?rewriteBatchedStatements=true 写在配置文件的url后面