一、利用反射及JDBC元数据编写通用的查询方法
1.ResultSetMetaData
定义:描述ResultSet的元数据对象,即从中可以获取到结果集中有多少列、列名是什么。
获取 ResultSetMetaData 对象:
调用 ResultSet 的 getMetaData() 方法
方法:
int getColumnCount():SQL语句中包含的列数
String getColumnLabel(int column):获取指定的列的别名,其中索引从1开始
2.通用的查询方法
/**
* 通用的查询方法:可以根据传入的SQL、Class对象返回SQL对应的对象
*
* @param clazz:
* 描述对象的类型
* @param sql:
* SQL语句,可能带占位符
* @param args:
* 填充占位符的可变参数
*/
public <T> T get(Class<T> clazz, String sql, Object... args) {
T entity = null;
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
// 1.获得ResultSet对象
conn = getConnection();
ps = conn.prepareStatement(sql);
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]);
}
rs = ps.executeQuery();
// 2.得到ResultSetMetaData对象
ResultSetMetaData rsmd = rs.getMetaData();
// 3.创建一个Map<String, Object>对象,键:SQL查询的列的别名;值:列的值
Map<String, Object> values = new HashMap<>();
// 4.处理结果集,利用ResultSetMetaData填充3对应的Map对象
if (rs.next()) {
for (int i = 0; i < rsmd.getColumnCount(); i++) {
String columnLabel = rsmd.getColumnLabel(i + 1);
Object columnValue = rs.getObject(i + 1);
values.put(columnLabel, columnValue);
}
}
// 5.若Map不为空集,利用反射创建 clazz 对应的对象
if (values.size() > 0) {
entity = clazz.newInstance();
// 6.遍历Map对象,利用反射为Class对象的对应的属性赋值。
for (Map.Entry<String, Object> entry : values.entrySet()) {
String fieldName = entry.getKey();
Object value = entry.getValue();
setFieldValue(entity, fieldName, value);
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
return entity;
}
/**
* 利用反射给具体对象的属性赋值
*/
public void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
Class<? extends Object> clazz = obj.getClass();
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
使用该通用方法:
/**
* 使用通用的查询方法
*/
public void testGet(){
String sql = "select id, name, email, birth "
+ "from customers where id = ?";
Customer customer = get(Customer.class, sql, 5);
System.out.println(customer);
}
二、DAO设计模式
1.定义:
DAO即 Data Access Object ,访问数据信息的类,包含了对数据的CRUD(create, read, update, delete),而不包含任何业务相关的信息。
2.作用:
实现功能的模块化,更有利于代码的保护和升级。
3.方法:
void update(String sql, Object ... args); //INSERT,UPDATE,DELETE操作都可以包含在其中
// INSERT,UPDATE,DELETE操作都可以包含在其中
public void update(String sql, Object... args) {
Connection conn = null;
PreparedStatement ps = null;
try {
conn = JDBCTools.getConnection();
ps = conn.prepareStatement(sql);
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]);
}
ps.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCTools.releaseDB(ps, null, conn);
}
}
public class JDBCTools {
public static void releaseDB(Statement stat, ResultSet rs, Connection conn){
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (stat != null) {
try {
stat.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public static Connection getConnection() throws Exception{
// 1.准备连接数据库的基本信息
String driver = null;
String jdbcUrl = null;
String user = null;
String password = null;
// 读取类路径下的jdbc.properties文件
InputStream in = JDBCTools.class.getClassLoader().getResourceAsStream("jdbc.properties");
Properties properties = new Properties();
properties.load(in);
driver = properties.getProperty("driver");
jdbcUrl = properties.getProperty("jdbcUrl");
user = properties.getProperty("user");
password = properties.getProperty("password");
// 2.加载数据库驱动程序(对应的Driver实现类中有注册驱动的静态代码块)
// DriverManager
// .registerDriver(Class.forName(driverClass).newInstance));
Class.forName(driver);
// 3.通过DriverManager的getConnection()方法 获取数据库连接
Connection conn = DriverManager.getConnection(jdbcUrl, user, password);
return conn;
}
}
<T> T get(Class<T> calzz, String sql, Object ... args); //查询一条记录,返回对应的对象
// 查询一条记录,返回对应的对象
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;
}
<T> List<T> getForList(Class<T> clazz, String sql, Object ... args); //查询多条记录,返回对应的对象的集合
// 查询多条记录,返回对应的对象的集合
public <T> List<T> getForList(Class<T> clazz, String sql, Object... args) {
List<T> list = new ArrayList<>();
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
//1.得到结果集
conn = JDBCTools.getConnection();
ps = conn.prepareStatement(sql);
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]);
}
rs = ps.executeQuery();
//2.处理结果集,得到Map的List,其中一个Map对象对应一条记录
List<Map<String, Object>> values = handleResultSetToMapList(rs);
//3.把Map的List转为clazz对应的List
list = transferMapListToBeanList(clazz, values);
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCTools.releaseDB(ps, rs, conn);
}
return list;
}
/**
* 将Map对应的List转换为clazz对应的List
* @param clazz
* @param values
* @return
* @throws Exception
*/
private <T> List<T> transferMapListToBeanList(Class<T> clazz, List<Map<String, Object>> values)
throws Exception {
List<T> result = new ArrayList<T>();
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 fieldName = entry.getKey();
Object value = entry.getValue();
JdbcTest.setFieldValue(bean, fieldName, value);
}
result.add(bean);
}
}
return result;
}
/**
* 处理结果集,得到Map的一个List,其中一个Map对象代表一条记录
*
* @param rs
* @return
* @throws SQLException
*/
private List<Map<String, Object>> handleResultSetToMapList(ResultSet rs) throws SQLException {
List<String> columnLabels = getColumnLabels(rs);
List<Map<String, Object>> values = new ArrayList<>();
Map<String, Object> map = null;
while (rs.next()) {
map = new HashMap<>();
for (String label : columnLabels) {
Object ColumnValue = rs.getObject(label);
map.put(label, ColumnValue);
}
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;
}
<E> E getForValue(String sql, Object ... args); //返回某条记录的某一个字段的值或者一个统计的值(统计一共有多少条记录等)
// 返回某条记录的某一个字段的值或者一个统计的值(统计一共有多少条记录或最大的值等)
public <E> E getForValue(String sql, Object... args) {
// 得到结果集:该结果集应该只有一行,且只有一列
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JDBCTools.getConnection();
ps = conn.prepareStatement(sql);
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]);
}
rs = ps.executeQuery();
if (rs.next()) {
return (E) rs.getObject(1);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCTools.releaseDB(ps, rs, conn);
}
return null;
}
4.使用Beanutils工具类操作JavaBean
JavaBean 通过提供符合一致性设计模式的公共方法将内部域暴露成员属性,通过getter和setter方法来定义属性的get和set方法。
可以通过一个apache的开源工具包 beanutils 来操作Java类的属性。
要先导入commons-logging包和commons-beanutils包到类路径下。
方法:setProperty(Object bean, String name, Object value):给对象bean的名字为name的属性赋value的值
getProperty(Object bean, String name):返回对象bean的名为name的属性的值
实例:
public void BeanutilsTest() throws Exception{
Object obj = new Customer();
System.out.println(obj);
//Customer [id=0, name=null, email=null, birth=null]
BeanUtils.setProperty(obj, "id", "123");
System.out.println(obj);
//Customer [id=123, name=null, email=null, birth=null]
Object value = BeanUtils.getProperty(obj, "id");
System.out.println(value);//123
}
注:上面DAO的方法中利用反射给对象的属性赋值都可以换成beanutils的方法来赋值
//JdbcTest.setFieldValue(entity, fieldName, value);
BeanUtils.setProperty(entity, fieldName, value);
三、JDBC的元数据
1.DataBaseMetaData
是在Connection对象上获得的(DataBaseMetaData dbmd = connnection.getMetaData()),DataBaseMetaData类的对象实现了获得有关数据库管理系统的各种信息的方法 ,包括数据库的各个表,表中的各个列,数据类型,触发器,存储方面等各方面信息。
2.ResultSetMetaData
是在ResultSet对象上获得的,实现了获得结果集的列数和列的别名等方法。
四、处理Blob
1.定义:
LOB,即Large Objects(大对象),是用来存储大量的二进制和文本数据的一种数据类型(一个LOB字段可以存储多达4GB的数据)
2.分类:
内部LOB:
BLOB(二进制数据)
CLOB(单字节字符数据)
NCLOB(多字节字符数据)
CLOB和NCLOB适合存储超长的文本数据。BLOB适合处理大量的二进制数据,如图像,视频,音频,文件等。
外部LOB:
BFILE
BFILE的数据是只读的,不参与事务。可帮助用户管理大量的由外部程序访问的文件。
3.实例:
/**
* 插入Blob类型的数据必须用PreparedStatement,因为Blob类型的数据无法使用字符串拼写
*/
public void testInsertBlob() {
Connection conn = null;
PreparedStatement ps = null;
try {
conn = JDBCTools.getConnection();
String sql = "INSERT INTO customers (NAME, EMAIL, BIRTH,PICTURE) " + "VALUES(?,?,?,?)";
ps = conn.prepareStatement(sql);
ps.setString(1, "AAA");
ps.setString(2, "abc@163.com");
ps.setDate(3, new Date(new java.util.Date().getTime()));
InputStream is = new FileInputStream("123.jpg");
ps.setBlob(4, is);
// 2)调用executeUpdate(sql)
ps.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCTools.releaseDB(null, ps, conn);
}
}