欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

JDBC(一)

程序员文章站 2022-05-03 08:57:29
...

一、持久化是什么

持久化(persistence):把数据保存到可掉电式存储设备中以供之后使用。持久化的主要应用是将内存中的数据存储在关系型数据库中,当然也可以存储在磁盘文件、XML数据文件中。

二、JDBC是什么

⑴JDBC(Java Database Connectivity)是一个独立于特定数据库管理系统、通用的SQL数据库存取和操作的公共接口。
JDBC为访问不同的数据库提供了一种统一的途径。JDBC的目标是使Java程序员使用JDBC可以连接任何提供了JDBC驱动程序的数据库系统。

⑵JDBC接口(API)包括两个层次:
    面向应用的API:Java API,抽象接口,供应用程序开发人员使用(连接数据库,执行SQL语句,获得结果)。
    面向数据库的API:Java Driver API,供开发商开发数据库驱动程序用。
JDBC API 是一系列的接口,它使得应用程序能够进行数据库联接,执行SQL语句,并且得到返回结果。

⑶Java.sql.Driver 接口是所有 JDBC 驱动程序需要实现的接口,由数据库厂商提供实现,能从其中获取数据库连接。
在程序中不需要直接去访问实现了 Driver 接口的类,而是由驱动程序管理器类(java.sql.DriverManager)去调用这些Driver实现。使用DriverManager可以通过重载的getConnection()方法获取数据库连接,较为方便。还可以同时管理多个驱动程序(调用getConnection()方法时传入的参数不同)。

⑷JDBC URL 用于标识一个被注册的驱动程序,驱动程序管理器通过这个 URL 选择正确的驱动程序,从而建立到数据库的连接。对于不同的数据库有不同的JDBC URL,这个可以在网上查到。
JDBC(一)
MySQL:jdbc:mysql://host:port/dbname
Oracle: jdbc:oracle:thin:@host:port:dbname
SQL Server: jdbc:microsoft:sqlserver://host:1433;DatabaseName=dbname
如果涉及到中文乱码问题,最好设置一下编码:在dbname后面加上?useUnicode=true&characterEncoding=UTF-8

三、JDBC访问数据库步骤简介

JDBC(一)

四、JDBC访问数据库步骤详解

⑴准备数据库连接的4个字符串

属性文件对应 Java 中的 Properties 类,可以使用类加载器加载 bin 目录(类路径下)的文件
  ①创建Properties对象
Properties properties=new Properties();
  ②获取jdbc.properties对应的输入流
InputStream in = this.getClass().getClassLoader().getResourceAsStream("jdbc.properties");
  ③加载②对应的输入流
properties.load(in);
  ④具体决定user,password,jdbcUrl,driver字符串
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String jdbcUrl = properties.getProperty("jdbcUrl");
String driver = properties.getProperty("driver");

⑵加载数据库驱动程序

数据库驱动程序对应的Driver实现类中有注册驱动的静态代码块
现在版本的数据库驱动程序不需要手动加载了,原因见JDC(二)。
Class.forName(driver即JDBC 驱动的全类名)

⑶建立连接
调用 DriverManager类的 getConnection()方法获取数据库连接
DriverManager.getConnection(jdbcUrl, user, password);

⑷准备要执行的SQL语句
 String SQL="  "

⑸创建用于执行 SQL 语句的对象:Statement,PrepatedStatement,CallableStatement

①Statement

通过调用 Connection 对象的 createStatement()方法创建Statement对象,该对象用于执行静态的 SQL 语句,并且返回执行结果

 Statement的三种执行方法:
     boolean execute (String SQL) : 如果可以检索到ResultSet对象,则返回一个布尔值true; 否则返回false。使用此方法执行SQLDDL语句或需要使用真正的动态SQL,可使用于执行创建数据库,创建表的SQL语句等等。
    int executeUpdate (String SQL): 返回受SQL语句执行影响的行数。使用此方法执行预期会影响多行的SQL语 句,例如:INSERT,UPDATE或DELETE语句。
     ResultSet executeQuery(String SQL):返回一个ResultSet对象。 当您希望获得结果集时,请使用此方法,就像使用SELECT语句一样。

②ResultSet

ResultSet对象以逻辑表格的形式封装了执行数据库操作的结果集(数据表),ResultSet接口由数据库厂商实现。
ResultSet对象维护了一个指向当前数据行的游标,初始的时候,游标在第一行之前,可以通过 ResultSet 对象的next()方法检测下一行是否有效,若有效,方法返回true,游标向下移动一行。
使用getXxx(int columnIndex / String columnName)获取每一列的值

③SQL 注入攻击

SQL 注入是利用某些系统没有对用户输入的数据进行充分的检查,而在用户输入数据中注入非法的 SQL 语句段或命令,从而利用系统的 SQL 引擎完成恶意行为的做法
对于 Java 而言,要防范 SQL 注入,只要用 PreparedStatement 取代 Statement 就可以了

④PreparedStatement

PreparedStatement 接口是 Statement 的子接口,它表示一条预编译过的 SQL 语句
PreparedStatement 能最大可能提高性能:DBServer会对预编译语句提供性能优化。因为预编译语句有可能被重复调用,所以语句在被DBServer的编译器编译后的执行代码被缓存下来,那么下次调用时只要是相同的预编译语句就不需要编译,只要将参数直接传入编译过的语句执行代码中就会得到执行。
PreparedStatement 可以防止 SQL 注入,而且代码的可读性和可维护性更好。

可以通过调用 Connection 对象的 preparedStatement()方法(必须传入一个SQL)获取 PreparedStatement 对象
PreparedStatement 对象所代表的 SQL 语句中的参数用问号(?)(占位符)来表示,调用 PreparedStatement 对象的 setXXX()方法来设置这些参数。setXXX() 方法有两个参数,第一个参数是要设置的 SQL 语句中的参数的索引(从 1 开始),第二个是设置的 SQL 语句中的参数的值
使用execute()、executeUpdate()或executeQuery()方法时,不用再传入SQL参数

⑤CallableStatement

如何使用 JDBC 调用存储在数据库中的函数或存储过程
1. 通过 Connection 对象的 prepareCall()方法创建一个 CallableStatement 对象的实例.
在使用 Connection 对象的 preparedCall() 方法时,需要传入一个 String 类型的字符串, 该字符串用于指明如何调用存储过程.
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);

⑥元数据

元数据是本身固有的特性

DatabaseMetaData类(JDBC 的元数据) :调用Connection对象的getMetaData()方法得到DataBaseMetaData对象
DatabaseMetaData 类中提供了许多方法用于获得数据源的各种信息,通过这些方法可以非常详细的了解数据库的信息

ResultSetMetaData 类(JDBC 的元数据):调用ResultSet对象的getMetaData()方法得到ResultSetMetaData对象
可用于获取关于 ResultSet 对象中列的类型和属性信息的对象
String getColumnName(int?column):获取指定列的名称
int getColumnCount():返回当前 ResultSet 对象中的列数。

⑹关闭数据库资源

Connection、Statement、ResultSet都是应用程序和数据库服务器的连接资源,使用后一定要关闭,关闭的顺序是: 先关闭后获取的或说是由里向外关闭。
调用相应的close()方法即可

五、示例代码

JDBC工具类

package com.atguigu.jdbc;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

/**
 * JDBC 的工具类
 * 
 * 其中包含: 获取数据库连接, 关闭数据库资源等方法.
 */
public class JDBCTools {

	public static Connection getConnection() throws Exception {
		Properties properties = new Properties();
		InputStream inStream = JDBCTools.class.getClassLoader()
				.getResourceAsStream("jdbc.properties");
		properties.load(inStream);

		// 1. 准备获取连接的 4 个字符串: user, password, jdbcUrl, driverClass
		String user = properties.getProperty("user");
		String password = properties.getProperty("password");
		String jdbcUrl = properties.getProperty("jdbcUrl");
		String driverClass = properties.getProperty("driverClass");

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

		// 3. 调用
		// DriverManager.getConnection(jdbcUrl, user, password)
		// 获取数据库连接
		Connection connection = DriverManager.getConnection(jdbcUrl, user,
				password);
		return connection;
	}

	public static void releaseDB(ResultSet resultSet, Statement statement,
			Connection connection) {

		if (resultSet != null) {
			try {
				resultSet.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();
			}
		}
	}
}

一个简单的通用的类DAO

package com.atguigu.jdbc;

import java.lang.reflect.InvocationTargetException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.beanutils.BeanUtils;

public class DAO {

	// INSERT, UPDATE, DELETE 操作都可以包含在其中
	public void update(String sql, Object... args) {
		Connection connection = null;
		PreparedStatement preparedStatement = null;

		try {
			connection = JDBCTools.getConnection();
			preparedStatement = connection.prepareStatement(sql);

			for (int i = 0; i < args.length; i++) {
				preparedStatement.setObject(i + 1, args[i]);
			}

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

	// 查询一条记录, 返回对应的对象
	public <T> T get(Class<T> clazz, String sql, Object... args) {
		List<T> result = getForList(clazz, sql, args);
		if(result.size() > 0){
			return result.get(0);
		}
		
		return null;
	}

	/**
	 * 传入 SQL 语句和 Class 对象, 返回 SQL 语句查询到的记录对应的 Class 类的对象的集合
	 * @param clazz: 对象的类型
	 * @param sql: SQL 语句
	 * @param args: 填充 SQL 语句的占位符的可变参数. 
	 * @return
	 */
	public <T> List<T> getForList(Class<T> clazz, 
			String sql, Object... args) {

		List<T> list = new ArrayList<>();

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

		try {
			//1. 得到结果集
			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. 处理结果集, 得到 Map 的 List, 其中一个 Map 对象
			//就是一条记录. Map 的 key 为 reusltSet 中列的别名, Map 的 value
			//为列的值. 
			List<Map<String, Object>> values = 
					handleResultSetToMapList(resultSet);
			
			//3. 把 Map 的 List 转为 clazz 对应的 List
			//其中 Map 的 key 即为 clazz 对应的对象的 propertyName, 
			//而 Map 的 value 即为 clazz 对应的对象的 propertyValue
			list = transfterMapListToBeanList(clazz, values);

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

		return list;
	}

	public <T> List<T> transfterMapListToBeanList(Class<T> clazz,
			List<Map<String, Object>> values) throws InstantiationException,
			IllegalAccessException, InvocationTargetException {

		List<T> result = new ArrayList<>();

		T bean = null;

		if (values.size() > 0) {
			for (Map<String, Object> m : values) {
				bean = clazz.newInstance();
				for (Map.Entry<String, Object> entry : m.entrySet()) {
					String propertyName = entry.getKey();
					Object value = entry.getValue();

					BeanUtils.setProperty(bean, propertyName, value);
				}
				// 13. 把 Object 对象放入到 list 中.
				result.add(bean);
			}
		}

		return result;
	}

	/**
	 * 处理结果集, 得到 Map 的一个 List, 其中一个 Map 对象对应一条记录
	 * 
	 * @param resultSet
	 * @return
	 * @throws SQLException
	 */
	public List<Map<String, Object>> handleResultSetToMapList(
			ResultSet resultSet) throws SQLException {
		// 5. 准备一个 List<Map<String, Object>>:
		// 键: 存放列的别名, 值: 存放列的值. 其中一个 Map 对象对应着一条记录
		List<Map<String, Object>> values = new ArrayList<>();

		List<String> columnLabels = getColumnLabels(resultSet);
		Map<String, Object> map = null;

		// 7. 处理 ResultSet, 使用 while 循环
		while (resultSet.next()) {
			map = new HashMap<>();

			for (String columnLabel : columnLabels) {
				Object value = resultSet.getObject(columnLabel);
				map.put(columnLabel, value);
			}

			// 11. 把一条记录的一个 Map 对象放入 5 准备的 List 中
			values.add(map);
		}
		return values;
	}

	/**
	 * 获取结果集的 ColumnLabel 对应的 List
	 * 
	 * @param rs
	 * @return
	 * @throws SQLException
	 */
	private List<String> getColumnLabels(ResultSet rs) throws SQLException {
		List<String> labels = new ArrayList<>();

		ResultSetMetaData rsmd = rs.getMetaData();
		for (int i = 0; i < rsmd.getColumnCount(); i++) {
			labels.add(rsmd.getColumnLabel(i + 1));
		}

		return labels;
	}

	// 返回某条记录的某一个字段的值 或 一个统计的值(一共有多少条记录等.)
	public <E> E getForValue(String sql, Object... args) {
		
		//1. 得到结果集: 该结果集应该只有一行, 且只有一列
		Connection connection = null;
		PreparedStatement preparedStatement = null;
		ResultSet resultSet = null;

		try {
			//1. 得到结果集
			connection = JDBCTools.getConnection();
			preparedStatement = connection.prepareStatement(sql);

			for (int i = 0; i < args.length; i++) {
				preparedStatement.setObject(i + 1, args[i]);
			}

			resultSet = preparedStatement.executeQuery();
			
			if(resultSet.next()){
				return (E) resultSet.getObject(1);
			}
		} catch(Exception ex){
			ex.printStackTrace();
		} finally{
			JDBCTools.releaseDB(resultSet, preparedStatement, connection);
		}
		//2. 取得结果
		
		return null;
	}
}

 DAO的测试类

package com.atguigu.jdbc;

import static org.junit.Assert.*;

import java.sql.Date;
import java.util.List;

import org.junit.Test;

public class DAOTest {
	DAO dao = new DAO();
	
	@Test
	public void testUpdate() {
		String sql = "INSERT INTO customers(name, " +
				"email, birth) VALUES(?,?,?)";
		dao.update(sql, "XiaoMing", "aaa@qq.com", 
				new Date(new java.util.Date().getTime()));
	}

	@Test
	public void testGet() {
		String sql = "SELECT flow_id flowId, type, exam_card examCard, " +
				"id_card idCard, student_name studentName, location, " +
				"grade FROM examstudent WHERE flow_id = ?";
		Student student = dao.get(Student.class, sql, 5);
		
		System.out.println(student); 
	}

	@Test
	public void testGetForList() {
		String sql = "SELECT flow_id flowId, type, exam_card examCard, " +
				"id_card idCard, student_name studentName, location, " +
				"grade FROM examstudent";
		
		List<Student> students = dao.getForList(Student.class, sql);
		System.out.println(students); 
	}

	@Test
	public void testGetForValue() {
		String sql = "SELECT exam_card FROM examstudent " +
				"WHERE flow_id = ?";
		String examCard = dao.getForValue(sql, 5);
		System.out.println(examCard); 
		
		sql = "SELECT max(grade) FROM examstudent";
		int grade = dao.getForValue(sql);
		System.out.println(grade); 
	}
}


 

相关标签: JDBC