JDBC 学习笔记(一)

JDBC 学习笔记(一)


文章目录


JDBC 介绍

定义

  JDBC(Java Database Connectivity)是一个独立于特定数据库管理系统、通用的SQL数据库存取和操作的公共接口(一组API),定义了用来访问数据库的标准Java类库,使用这些类库可以以一种标准的方法、方便地访问数据库资源。任何提供了JDBC驱动程序的数据库系统都可以使用Java连接。开发者可以通过直接使用JDBC提供的接口,方便的对数据库进行增删改查操作。

JDBC 结构

JDBC接口包括两方面:

   · 面向应用的API:抽象接口,提供给开发人员使用;
   · 面向数据库的API:供开发商开发数据库驱动使用(标准化数据库操作)。

JDBC连接

要进行JDBC的连接,分为如下几步:

  · 加载驱动
  · 注册驱动
  · 获取连接

首先加载驱动,使用Class.forName()方法,通过此方法可以将对应的驱动类加载到内存中,对于Mysql数据库使用的是"com.mysql.jdbc.Driver"驱动,也就是一个类。
  在这里有必要说明一下,Class.forName()返回与给定的字符串名称相关联类或接口的Class对象。是一个静态方法,同样可以用来加载类。该方法有两种形式:Class.forName(String name, boolean initialize, ClassLoader loader)和 Class.forName(String className)。第一种形式的参数 name表示的是类的全名;initialize表示是否初始化类;loader表示加载时使用的类加载器。第二种形式则相当于设置了参数 initialize的值为 true,loader的值为当前类的类加载器。下面来观察一下Driver类的定义情况,类中包含了一个静态代码块,在类加载的时候调用了DriverManager类中的registerDriver方法,参数传入一个Driver()类实例,实现驱动的注册功能,换句话说,在使用ClassforName()进行驱动的加载时,就已经自动的进行了驱动的注册。

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    public Driver() throws SQLException {
    }

    static {
        try {
        //创建一个MysqlDriver实例,然后注册到DriverManager中
            DriverManager.registerDriver(new Driver());
        } catch (SQLException var1) {
            throw new RuntimeException("Can't register driver!");
        }
    }
}

最后一步获取连接,使用DriverManager类中的getConnect()方法建立数据库的连接,该方法的定义如下:

    public static Connection getConnection(String url,
        String user, String password) throws SQLException {
        java.util.Properties info = new java.util.Properties();

        if (user != null) {
            info.put("user", user);
        }
        if (password != null) {
            info.put("password", password);
        }

        return (getConnection(url, info, Reflection.getCallerClass()));
    }

  因为getConnection是静态方法,所以可以通过DriverManager.getConnection(url,user, password)来调用此方法,实现数据库的连接获取。
  在JDBC的连接过程中需要三个要素:Driver接口的实现类、URL、用户名和密码。
因此可以使用配置文件存储这三个要素,在代码中加载配置文件,实现数据库的连接操作。
配置文件(jdbc.properties)如下:

#获取数据
user=root
password=159613
url=jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true
driverClass=com.mysql.jdbc.Driver

使用配置文件实现JDBC的连接操作:

public void testConnection5()throws Exception{
        //读取配置文件中的4个基本信息
        InputStream is = ConnectTest.class.getClassLoader().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");

        //加载驱动
        Class.forName(driverClass);
        //获取连接
        Connection conn = DriverManager.getConnection(url,user,password);
        System.out.println(conn);
    }

    public static void main(String[] args) throws Exception {
        ConnectTest con = new ConnectTest();
        con.testConnection5();
    }

这样就实现了一个JDBC的连接。

使用Preparedment实现CRUD操作

数据库的访问方式

  在java.sql包中有三个接口实现了对数据库的调用,分别是:Statement、PreparedStatement、CallableStatement

Statement

  使用createStatement()方法创建对象,用于执行静态sql语句。Statement在操作数据表时或存在弊端:

1.需要进行拼串操作
2.存在sql注入问题

观察示例代码:

package zhang.da.pao;

import java.io.InputStream;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
import java.util.Scanner;

import org.junit.Test;

public class StatementTest {

	// 使用Statement的弊端:需要拼写sql语句,并且存在SQL注入的问题
	@Test
	public void testLogin() {
		Scanner scan = new Scanner(System.in);

		System.out.print("用户名:");
		String userName = scan.nextLine();
		System.out.print("密   码:");
		String password = scan.nextLine();

		// SELECT user,password FROM user_table WHERE USER = '1' or ' AND PASSWORD = '
		// ='1' or '1' = '1';
		String sql = "SELECT user,password FROM user_table WHERE USER = '" + userName + "' AND PASSWORD = '" + password
				+ "'";
		User user = get(sql, User.class);
		if (user != null) {
			System.out.println("登陆成功!");
		} else {
			System.out.println("用户名或密码错误!");
		}
	}

	// 使用Statement实现对数据表的查询操作
	public <T> T get(String sql, Class<T> clazz) {
		T t = null;

		Connection conn = null;
		Statement st = null;
		ResultSet rs = null;
		try {
			// 1.加载配置文件
			InputStream is = StatementTest.class.getClassLoader().getResourceAsStream("jdbc.properties");
			Properties pros = new Properties();
			pros.load(is);

			// 2.读取配置信息
			String user = pros.getProperty("user");
			String password = pros.getProperty("password");
			String url = pros.getProperty("url");
			String driverClass = pros.getProperty("driverClass");

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

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

			st = conn.createStatement();

			rs = st.executeQuery(sql);

			// 获取结果集的元数据
			ResultSetMetaData rsmd = rs.getMetaData();

			// 获取结果集的列数
			int columnCount = rsmd.getColumnCount();

			if (rs.next()) {

				t = clazz.newInstance();

				for (int i = 0; i < columnCount; i++) {
					// //1. 获取列的名称
					// String columnName = rsmd.getColumnName(i+1);

					// 1. 获取列的别名
					String columnName = rsmd.getColumnLabel(i + 1);

					// 2. 根据列名获取对应数据表中的数据
					Object columnVal = rs.getObject(columnName);

					// 3. 将数据表中得到的数据,封装进对象
					Field field = clazz.getDeclaredField(columnName);
					field.setAccessible(true);
					field.set(t, columnVal);
				}
				return t;
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			// 关闭资源
			if (rs != null) {
				try {
					rs.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
			if (st != null) {
				try {
					st.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}

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

		return null;
	}

}

  在上例中,使用Statement实现数据库的操作,sql语句需要进行字符串的拼接,另外,如果使用下面的sql语句,会产生sql注入问题:

SELECT user,password FROM user_table WHERE USER = '1' or ' AND PASSWORD = '='1' or '1' = '1';

所以要使用PreparedStatement接口来代替Statement接口。

PreparedStatement

  通过调用Connection对象的PreparedStatement(String sql)方法来获取PreparedStatement对象。
需要注意的是:Preparedstatement对象接收的sql语句中,sql包含的参数使用占位符:“?”来表示,使用setObject()等方法进行参数的设置,setObject()的参数有两个,第一个参数表示第几个“?”,第二个参数是需要设置的值。

增删改操作

数据库增加操作:

//向customer中添加一条记录
    public void testInsert() throws Exception {
        //1.读取配置文件中的4个基本信息
        InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("src/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 conn = DriverManager.getConnection(url,user,password);
        //System.out.println(conn);
        //4.预编译sql语句,返回preparedStatement的实例
        String sql = "insert into customers(name,email,birth)values(?,?,?)";//?;占位符
        PreparedStatement ps = conn.prepareStatement(sql);
        //5.填充占位符
        ps.setString(1,"jiege");
        ps.setString(2,"z18014702106@163.com");
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-mm-dd");
        Date date = sdf.parse("1996-12-24");
        ps.setDate(3,new java.sql.Date(date.getTime()));
        //6.执行sql
        ps.execute();
        //7.资源的关闭
        ps.close();
        conn.close();
    }

此时就实现了数据库的数据增加操作,共分为七步完成:

  1.读取配置文件中的4个基本信息
  2.加载驱动
  3.获取链接
  4.预编译sql语句,返回preparedStatement的实例
  5.填充占位符
  6.执行sql
  7.资源的关闭

  对于数据的增删改来说,仅仅是sql语句的内容不同,没有返回数据,所以可以将代码修改为一般性的增删改通用代码,而且对于每次进行增删改操作,数据库的连接,资源的关闭操作都是相同的,可以将这些操作定义在工具类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 conn = DriverManager.getConnection(url,user,password);
        return conn;
    }
    /*
    * 关闭资源
    * */
    public static void closeResource(Connection conn, PreparedStatement ps)throws Exception{
        if(ps!=null){
            ps.close();
        }
        if(conn!=null){
            conn.close();
        }
    }
    public static void closeResource(Connection conn, PreparedStatement ps,ResultSet resultSet)throws Exception{
        if(ps!=null){
            ps.close();
        }
        if(conn!=null){
            conn.close();
        }
        if(resultSet!=null){
            resultSet.close();
        }

    }
}

  此时可以使用JDBCUtils.getConnection()方法获取Connection对象,使用JDBCUtils.close(Connection connection,PreparedStatement ps)关闭资源。
下面是通用的增删改操作代码:

 public void sqlTest(String sql,Object ...args)throws Exception{
            //1.获取链接
            Connection conn = JDBCUtils.getConnection();
            //2.预编译
            PreparedStatement ps = conn.prepareStatement(sql);
            //3.填充占位符
            for(int i =0;i<args.length;i++){
                ps.setObject(i+1,args[i]);
            }
            //4.执行
            ps.execute();
            //5.关闭资源
            JDBCUtils.closeResource(conn,ps);
    }

  这样代码一下子变得精简起来了,这里我们已经实现了多数据库的增删改操作,还有一个很重要的查询操作,因为数据查询涉及了返回值的问题,所以需要单独拿出来说明。

数据库的查询操作

  数据库的查询操作返回了一个结果集,使用PreparedStatement类中的executeQuery()获取结果集resultSet,因为获取的结果集为一个表数据,包含了属性名和属性值,我们使用一个对象来存储获取到的结果集,需要知道的是结果集的列数,使用ResultSetMetaData metaData = resultSet.getMetaData()获取元数据(包括列名和列数),metaData.getClumnCount()获取,还需要知道结果集的列属性名称:使用:metaData.getColumnLabel(i+1)获取第i行对应的属性值。使用反射的方法,通过属性名来获取对应的存储类中的属性,给对象的相应属性赋值。
示例代码如下:

public <T> T testQuery(Class<T> clazz,String sql,Object ...args )throws Exception{
        //获取连接
        Connection conn = JDBCUtils.getConnection();
        //预编译
        PreparedStatement ps = conn.prepareStatement(sql);
        //填充占位符
        for(int i = 0;i<args.length;i++){
            ps.setObject(i+1,args[i]);
        }
        //获取结果集
        ResultSet resultSet = ps.executeQuery();
        //获取元数据
        ResultSetMetaData metaData = resultSet.getMetaData();
        if(resultSet.next()){
            //获取结果集的列数
            int columnCount = metaData.getColumnCount();
            //创建对象,接收数据
            T t = clazz.getDeclaredConstructor().newInstance();
            for(int i = 0;i <columnCount;i++){
                //获取每列的元素名
                String columnLabel = metaData.getColumnLabel(i+1);
                //获取列值
                Object value = resultSet.getObject(i + 1);

                //通过反射存入数据
                Field declaredField = clazz.getDeclaredField(columnLabel);
                declaredField.setAccessible(true);
                declaredField.set(t,value);
            }
            return t;
        }
        return null;
    }
上一篇:留学生如何把控好Essay写作结构


下一篇:JDBCUtils.java