一、持久化是什么
持久化(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);
}
}