JDBC(一)

一、持久化是什么

持久化(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,这个可以在网上查到。

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访问数据库步骤详解

⑴准备数据库连接的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", "xiaoming@atguigu.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); 
	}
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值