笔记:JDBC

一. 概念

JDBC(Java Database Connectivity)是一个独立于特定数据库管理系统、通用的 SQL 数据库存取和操作的公共接口

目标是使 Java 程序员使用 JDBC 可以连接任何提供了 JDBC 驱动程序的数据库系统

二. 通过 JDBC 连接数据库

1. 通常的连接方式

/**
 * Driver 是一个接口: 数据库厂商必须提供实现的接口. 能从其中获取数据库连接.
 * 可以通过 Driver 的实现类对象获取数据库连接.
 * 
 * 1. 加入 mysql 驱动
 * 1). 解压 mysql-connector-java-5.1.7.zip
 * 2). 在当前项目下新建 lib 目录
 * 3). 把 mysql-connector-java-5.1.7-bin.jar 复制到 lib 目录下
 * 4). 右键 build-path , add to buildpath 加入到类路径下
 * 
 */
@Test
public void test() throws SQLException{
	//1. 创建一个 Driver 实现类的对象
	Driver driver = new com.mysql.cj.jdbc.Driver();

	//2. 准备连接数据库的基本信息: url, user, password
	String url="jdbc:mysql://localhost:3306/test";
	Properties info = new Properties();
	info.put("user", "root");
	info.put("password", "");
	//JDBC 5.1.28之前不会存在时区的问题,之后需要设置时区,不然会出差 
	info.put("serverTimezone", "GMT+8");

	//3. 调用 Driver 接口的 connect(url, info) 获取数据库连接
	Connection connect = driver.connect(url, info);
	System.out.println(connect);
}

/**
 * 编写一个通用的方法, 在不修改源程序的情况下, 可以获取任何数据库的连接
 * 解决方案: 把数据库驱动 Driver 实现类的全类名、url、user、password 放入一个
 * 配置文件中, 通过修改配置文件的方式实现和具体的数据库解耦. 
 * @throws Exception 
 */
public Connection getConnection() throws Exception{
	String driverClass=null;
	String url=null;
	String user=null;
	String password=null;

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

	//通过反射常见 Driver 对象. 
	Driver driver=(Driver) Class.forName(driverClass).newInstance();
	Properties info = new Properties();
	info.put("user", user);
	info.put("password", password);

	//通过 Driver 的 connect 方法获取数据库连接. 
	Connection connect = driver.connect(url, info);
	return connect;
}

@Test
public void test2() throws Exception{
	Connection connection  = getConnection();
	System.out.println(connection);
}
----------------------------------------------------------------------------------------

jdbc.properties 中的数据

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8
user=root
password=

2.DriverManager 方式

/**
 * DriverManager 是驱动的管理类. 
 * 1). 可以通过重载的 getConnection() 方法获取数据库连接. 较为方便
 * 2). 可以同时管理多个驱动程序: 若注册了多个数据库连接, 则调用 getConnection()
 * 方法时传入的参数不同, 即返回不同的数据库连接。 
 * 
 */
public Connection getConnection2() throws Exception{
	//1. 准备连接数据库的 4 个字符串. 
	//1). 创建 Properties 对象
	Properties properties = new Properties();
	
	//2). 获取 jdbc.properties 对应的输入流
	InputStream in = 
			this.getClass().getClassLoader().getResourceAsStream("jdbc.properties");
	
	//3). 加载 2) 对应的输入流
	properties.load(in);
	
	//4). 具体决定 user, password 等4 个字符串. 
	String user = properties.getProperty("user");
	String password = properties.getProperty("password");
	String jdbcUrl = properties.getProperty("url");
	String driver = properties.getProperty("driver");
	
	//2. 加载数据库驱动程序(对应的 Driver 实现类中有注册驱动的静态代码块.)
	Class.forName(driver);
	
	//3. 通过 DriverManager 的 getConnection() 方法获取数据库连接. 
	return DriverManager.getConnection(jdbcUrl, user, password);
}

三. 通过 Statement 执行添加修改删除操作

/**
 * 通过 JDBC 向指定的数据表中插入一条记录. 
 * 
 * 1. Statement: 用于执行 SQL 语句的对象
 * 1). 通过 Connection 的 createStatement() 方法来获取
 * 2). 通过 executeUpdate(sql) 可以执行 SQL 语句.
 * 3). 传入的 SQL 可以是 INSRET, UPDATE 或 DELETE. 但不能是 SELECT
 * 
 * 2. Connection、Statement 都是应用程序和数据库服务器的连接资源. 使用后一定要关闭.
 * 需要在 finally 中关闭 Connection 和 Statement 对象. 
 * 
 * 3. 关闭的顺序是: 先关闭后获取的. 即先关闭 Statement 后关闭 Connection
 */
@Test
public void testStatement() throws Exception{
	//1. 获取数据库连接
	Connection conn = null;
	Statement statement = null;
	
	try {
		conn = getConnection2();
		
		//3. 准备插入的 SQL 语句
		String sql = null;
		
//		sql = "INSERT INTO customers (NAME, EMAIL, BIRTH) " +
//				"VALUES('XYZ', '[email protected]', '1990-12-12')";
//		sql = "DELETE FROM customers WHERE id = 1";
		sql = "UPDATE customers SET name = 'TOM' " +
				"WHERE id = 4";
		System.out.println(sql);
		
		//4. 执行插入. 
		//1). 获取操作 SQL 语句的 Statement 对象: 
		//调用 Connection 的 createStatement() 方法来获取
		statement = conn.createStatement();
		
		//2). 调用 Statement 对象的 executeUpdate(sql) 执行 SQL 语句进行插入
		statement.executeUpdate(sql);
	} catch (Exception e) {
		e.printStackTrace();
	} finally{
		try {
			//5. 关闭 Statement 对象.
			if(statement != null)
				statement.close();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally{
			//2. 关闭连接
			if(conn != null)
				conn.close();							
		}
	}
	
}

四. 通过 ResultSet 执行查询操作

/**
 * ResultSet: 结果集. 封装了使用 JDBC 进行查询的结果. 
 * 1. 调用 Statement 对象的 executeQuery(sql) 可以得到结果集.
 * 2. ResultSet 返回的实际上就是一张数据表. 有一个指针指向数据表的第一样的前面.
 * 可以调用 next() 方法检测下一行是否有效. 若有效该方法返回 true, 且指针下移. 相当于
 * Iterator 对象的 hasNext() 和 next() 方法的结合体
 * 3. 当指针对位到一行时, 可以通过调用 getXxx(index) 或 getXxx(columnName)
 * 获取每一列的值. 例如: getInt(1), getString("name")
 * 4. ResultSet 当然也需要进行关闭. 
 */
@Test
public void testResultSet(){
	//获取 id=4 的 customers 数据表的记录, 并打印
	
	Connection conn = null;
	Statement statement = null;
	ResultSet rs = null;
	
	try {
		//1. 获取 Connection
		conn = JDBCTools.getConnection();
		System.out.println(conn);
		
		//2. 获取 Statement
		statement = conn.createStatement();
		System.out.println(statement);
		
		//3. 准备 SQL
		String sql = "SELECT id, name, email, birth " +
				"FROM customers";
		
		//4. 执行查询, 得到 ResultSet
		rs = statement.executeQuery(sql);
		System.out.println(rs);
		
		//5. 处理 ResultSet
		while(rs.next()){
			int id = rs.getInt(1);
			String name = rs.getString("name");
			String email = rs.getString(3);
			Date birth = rs.getDate(4);
			
			System.out.println(id);
			System.out.println(name);
			System.out.println(email);
			System.out.println(birth);
		}
		
	} catch (Exception e) {
		e.printStackTrace();
	} finally{
		//6. 关闭数据库资源. 
		JDBCTools.release(rs, statement, conn);
	}
	
}
@Test
public void testSelect() throws Exception{
	Connection connection = getConnection2();
	
	String sql="select * from customers where 1 =1 ";
	
	Statement statement = connection.createStatement();
	ResultSet resultSet = statement.executeQuery(sql);
	resultSet.last();
	if (resultSet.getRow()==0) {
		System.out.println("没有查到数据");
	}
	else{
		resultSet.beforeFirst();
		while (resultSet.next()) {
			System.out.println(resultSet.getString("name"));
		}
	}
	
	connection.close();
}

五. PreparedStatement 类

1. 概念

是 Statement 的子接口,可以传入带占位符的 SQL 语句,并且提供了补充占位符变量的方法

可以有效的禁止 SQL 注入

能最大可能提高性能

索引下标是从1开始的

String sql = "INSERT INTO customers (name, email, birth) VALUES(?,?,?)";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1, "ATGUIGU");
preparedStatement.setString(2, "[email protected]");
preparedStatement.setDate(3,new Date(new java.util.Date().getTime()));
preparedStatement.executeUpdate();// 此时不需要再传入 SQL 语句

六. 通用的查询方法

/**
 * 通用的查询方法:可以根据传入的 SQL、Class 对象返回 SQL 对应的记录的对象
 * @param clazz: 描述对象的类型
 * @param sql: SQL 语句。可能带占位符
 * @param args: 填充占位符的可变参数。
 * @return
 */
public <T> T get(Class<T> clazz, String sql, Object... args) {
	T entity = null;

	Connection connection = null;
	PreparedStatement preparedStatement = null;
	ResultSet resultSet = null;

	try {
		//1. 得到 ResultSet 对象
		connection = JDBCTools.getConnection();
		preparedStatement = connection.prepareStatement(sql);
		for (int i = 0; i < args.length; i++) {
			preparedStatement.setObject(i + 1, args[i]);
		}
		resultSet = preparedStatement.executeQuery();

		//2. 得到 ResultSetMetaData 对象
		ResultSetMetaData rsmd = resultSet.getMetaData();
		
		//3. 创建一个 Map<String, Object> 对象, 键: SQL 查询的列的别名, 
		//值: 列的值
		Map<String, Object> values = new HashMap<>();
		
		//4. 处理结果集. 利用 ResultSetMetaData 填充 3 对应的 Map 对象
		if(resultSet.next()){
			for(int i = 0; i < rsmd.getColumnCount(); i++){
				String columnLabel = rsmd.getColumnLabel(i + 1);
				Object columnValue = resultSet.getObject(i + 1);
				
				values.put(columnLabel, columnValue);
			}
		}
		
		//5. 若 Map 不为空集, 利用反射创建 clazz 对应的对象
		if(values.size() > 0){
			entity = clazz.newInstance();
			
			//5. 遍历 Map 对象, 利用反射为 Class 对象的对应的属性赋值. 
			for(Map.Entry<String, Object> entry: values.entrySet()){
				String fieldName = entry.getKey();
				Object value = entry.getValue();
				ReflectionUtils.setFieldValue(entity, fieldName, value);
			}
		}
		

	} catch (Exception e) {
		e.printStackTrace();
	} finally {
		JDBCTools.releaseDB(resultSet, preparedStatement, connection);
	}

	return entity;
}

七. DAO 设计模式

1. 访问数据信息的类。包含了对数据的CRUD,而不包含任何业务相关的信息

2. 使用 beanutils 工具类操作

①. 导入 commons-logging-1.1.1.jar 和 commons-beanutils-1.8.0.jar

②. 使用 beanutils.setProperty 和 beanutils.getProperty

八. 处理元数据

1. DatabaseMetaData 是描述数据库的元数据对象

@Test
public void test() {
	Connection connection=null;
	DatabaseMetaData data=null;
	ResultSet resultSet=null;
	
	try {
		connection=JDBCTools.getConnection();
		data=connection.getMetaData();
		// 获取数据库版本号
		System.out.println(data.getDatabaseMajorVersion());
		// 获取连接到数据库的用户名
		System.out.println(data.getUserName());
		// 得到有哪些数据库
		resultSet=data.getCatalogs();
		while (resultSet.next()) {
			System.out.println(resultSet.getString(1));
		}
	} catch (Exception e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}finally {
		JDBCTools.releaseDB(resultSet, null, connection);
	}
	
}

2. ResultSetMetaData 类

/**
 * ResultSetMetaData: 描述结果集的元数据. 
 * 可以得到结果集中的基本信息: 结果集中有哪些列, 列名, 列的别名等.
 * 结合反射可以写出通用的查询方法. 
 */
resultSet = preparedStatement.executeQuery();
Map<String, Object> values = new HashMap<String, Object>();
ResultSetMetaData rsmd = resultSet.getMetaData();

while(resultSet.next()){
	//2. 打印每一列的列名
	for(int i = 0; i < rsmd.getColumnCount(); i++){
		// 获取列的别名
		String columnLabel = rsmd.getColumnLabel(i + 1);
		// 获取列的值
		Object columnValue = resultSet.getObject(columnLabel);
		
		values.put(columnLabel, columnValue);
	}
}

九. 取得数据库自动生成的主键

/**
 * 取得数据库自动生成的主键
 */
@Test
public void testGetKeyValue() {
	
	Connection connection = null;
	PreparedStatement preparedStatement = null;
	
	try {
		connection = JDBCTools.getConnection();
		String sql = "INSERT INTO customers(name, email, birth)" +
				"VALUES(?,?,?)";
//		preparedStatement = connection.prepareStatement(sql);

		//使用重载的 prepareStatement(sql, flag) 
		//来生成 PreparedStatement 对象
		preparedStatement = connection.prepareStatement(sql, 
				Statement.RETURN_GENERATED_KEYS);
		
		preparedStatement.setString(1, "ABCDE");
		preparedStatement.setString(2, "[email protected]");
		preparedStatement.setDate(3, 
				new Date(new java.util.Date().getTime()));
		
		preparedStatement.executeUpdate();
		
		//通过 getGeneratedKeys() 获取包含了新生成的主键的 ResultSet 对象
		//在 ResultSet 中只有一列 GENERATED_KEY, 用于存放新生成的主键值.
		ResultSet rs = preparedStatement.getGeneratedKeys();
		if(rs.next()){
			System.out.println(rs.getObject(1));
		}
		
		ResultSetMetaData rsmd = rs.getMetaData();
		for(int i = 0; i < rsmd.getColumnCount(); i++){
			System.out.println(rsmd.getColumnName(i + 1)); 
		}
		
	} catch (Exception e) {
		e.printStackTrace();
	} finally{
		JDBCTools.releaseDB(null, preparedStatement, connection);
	}
	
}

猜你喜欢

转载自blog.csdn.net/qq_36797286/article/details/81431724