1. 数据库元数据
1.1 概述
元数据用来描述数据中的库结构、表结构、记录信息的数据
1.2 分类
三种元数据 | 说明以及获得方式 |
---|---|
数据库元数据 | 用来描述数据库信息:数据库使用版本,数据库使用驱动等。如何得到数据库元数据对象呢?通过Connection对象可以得到。 |
参数元数据 ParameterMetaData | 描述SQL语句中各种参数:占位符个数,每个参数的数据类型。 通过PreparedStatement对象可以得到参数元数据对象 |
结果集元数据 | 描述结果集:结果集有多少列,每列数据是什么类型等信息。 通过ResultSet对象可以得到参数元数据对象 |
ParameterMetaData接口中的方法
//通过预编译语句对象得到一个参数元数据对象
ParameterMetaData getParameterMetaData()
//得到 SQL 语句中占位符个数
int getParameterCount()
//得到指定占位符的数据类型,智能
//参数param:指定第几个占位符
String getParameterTypeName(int param)
-- MySQL 驱动对参数元数据的数据类型支持不理想 ,需要如下配置才能得到的参数的 MySQL 数据类型,而且只能得到 VARCHAR 类型 ,如果不配置这个参数则会出现异常。
jdbc:mysql://localhost:3306/ 数据 库名?generateSimpleParameterMetadata=true
ResultSetMetaData 接口中的方法
//得到结果集中一共有多少列
int getColumnCount()
//得到指定列的列名
String getColumnName(int column)
//得到指定列的数据类型名字
String getColumnTypeName(int column)
使用
/**
* 使用结果集元数据对象
*/
public class DemoResult {
public static void main(String[] args) throws SQLException {
//创建连接对象
Connection conn = DriverManager.getConnection("jdbc:mysql:///day20", "root", "root");
//创建预编译语句对象
PreparedStatement ps = conn.prepareStatement("SELECT id,name from student");
//执行查询语句
//得到结果集
ResultSet rs = ps.executeQuery();
//得到结果集元数据
ResultSetMetaData metaData = rs.getMetaData();
//调用元数据的方法
//得到一共有多少列
System.out.println("结果集中一共有" + metaData.getColumnCount() + "列");
//得到指定列的列名
System.out.println("第 2 列的列名:" + metaData.getColumnName(2));
//得到指定列的数据类型
System.out.println("第 1 列的数据类型:" + metaData.getColumnTypeName(1));
//关闭
rs.close();
ps.close();
conn.close();
}
}
1.3 自定义框架
可以通过元数据对JDBC进行封装,使JDBC更加容易使用。
案例需求:
修改 DruidUtils 工具类,使用元数据做一个通用的增删改的方法。
druid.properties配置文件
url=jdbc:mysql://localhost:3306/day19
username=root
password=awsed2zx
driverClassName=com.mysql.jdbc.Driver
initialSize=5
maxActive=10
maxWait=2000
DruidUtils工具类
package com.zmysna.utils;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.IOException;
import java.sql.*;
import java.util.Properties;
public class DruidUtils {
private static DataSource ds;
/**
加载配置文件,初始化DataSource
*/
static {
Properties pro = new Properties();
try {
pro.load(DruidUtils.class.getResourceAsStream("/druid.properties"));
ds = DruidDataSourceFactory.createDataSource(pro);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
用元数据实现的通用增删改方法
*/
public static int update(String sql, Object...params) {
try (
//得到连接对象
Connection conn = getConnection();
//创建预编译语句对象,有占位符
PreparedStatement ps = conn.prepareStatement(sql);) {
//通过参数元数据得到一共有多少个占位符
ParameterMetaData pmd = ps.getParameterMetaData();
int count = pmd.getParameterCount();
//使用循环来替换每个占位符
for (int i = 0; i < count; i++) {
ps.setObject((i + 1), params[i]);
}
//更新操作
return ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
return 0;
}
public static Connection getConnection() throws SQLException {
return ds.getConnection();
}
public static void close(Connection conn, Statement statement, ResultSet rs) {
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(statement != null) {
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public static void close(Connection conn, Statement statement) {
close(conn, statement,null);
}
}
2. JDBCTemplate
JdbcTemplate就是Spring对JDBC的封装,目的是使JDBC更加易于使用。JdbcTemplate是Spring的一部分。使用JdbcTemplate编程我们只需要做以下步骤:
-
提供SQL语句
-
提供占位符的真实值(可选)
2.1 使用步骤
- 导包
- 执行DML语句
public int update(final String sql, Object…args)
作用:执行增删改的操作
参数:
1) 要执行的SQL语句
2) 替换占位符的值
返回值:影响的行数
package com.zmysna;
import com.itheima.utils.DataSourceUtils;
import org.junit.Test;
import org.springframework.jdbc.core.JdbcTemplate;
/**
* 使用模板对象实现增删改操作
*/
public class Demo2DML {
//1) 创建JdbcTemplate对象,传入数据源对象。
JdbcTemplate template = new JdbcTemplate(DataSourceUtils.getDataSource());
//a) 向表中添加5条记录
@Test
public void testInsert() {
template.update("INSERT INTO student values(null,?,?,?)", "孙悟空", 1, "2000-11-11");
template.update("INSERT INTO student values(null,?,?,?)", "孙悟天", 1, "2000-11-11");
template.update("INSERT INTO student values(null,?,?,?)", "孙悟饭", 1, "2000-11-11");
template.update("INSERT INTO student values(null,?,?,?)", "牛魔王", 1, "2000-11-11");
template.update("INSERT INTO student values(null,?,?,?)", "白骨精", 0, "2000-11-11");
}
//b) 修改编号为2的学生名字和生日
@Test
public void testUpdate() {
int i = template.update("UPDATE student SET name=?, gender=?,birthday=? where id=?", "蜘蛛精", 0, "1985-10-02", 2);
System.out.println(i);
}
//c) 删除编号为5的记录
@Test
public void testDelete() {
int i = template.update("DELETE FROM student where id=?", 5);
System.out.println(i);
}
}
-
执行DQL语句
- 查询一条记录封装成Map对象
Map<String,Object> queryForMap(String sql, Object…args) 参数: 1) 要查询的SQL语句 2) 占位符的真实值 返回值: 一条记录,封装成Map对象。可以用于多表连接,可以查询一张表中几列。 注:如果查询的结果集没有记录,会抛出异常
练习:
import com.itheima.utils.DataSourceUtils; import org.junit.Test; import org.springframework.jdbc.core.JdbcTemplate; import java.util.Map; /** * 使用模板对象执行DQL语句 */ public class Demo3DQL { //创建模板对象 JdbcTemplate template = new JdbcTemplate(DataSourceUtils.getDataSource()); //查询一条记录,封装成Map对象 @Test public void testQueryForMap() { // 使用JdbcTemplate对象的queryForMap()方法查询结果 Map<String, Object> map = template.queryForMap("SELECT * from student where id=?", 2); // 返回一个Map对象,并且输出结果。 System.out.println(map); // 如果没有查询到结果会怎么样? 会出现异常 } }
- 查询一条记录,封装成一个实体类对象
<T> T queryForObject(String sql, RowMapper<T> rowMapper, Object... args) 参数: 1) 要执行的SQL语句 2) 这是一个接口,需要我们实现它,并且重写里面的方法,实现将一条结果集封装成一个实体类对象。 3) 占位符的真实值 返回值: 封装好的实体类对象,如果没有找到结果,会抛出异常。 //RowMapper接口中只有一个mapRow方法,需要重写, 可以使用其实现类BeanPropertyRowMapper。 T mapRow(ResultSet rs, int rowNum) throws SQLException 作用: 用于将查询到的结果集封装成一个对象 参数: 1) 从数据库中查询到的结果集,有可能是一条记录,也可能是多条记录 2) 行号,从0开始编号,表示当前这条记录在整个结果集中是第几行。 返回值: 封装好的对象 //BeanPropertyRowMapper类的构造方法 public BeanPropertyRowMapper(Class<T> mappedClass) 通过构造方法创建一个行映射对象,参数是:指定是的类型,如:Student.class,Employee.class
练习
//使用JdbcTemplate对象的queryForObject ()方法查询结果,方法中传入一个接口的匿名内部类 @Test public void testQueryForObject() { Student student = template.queryForObject("select * from student where id=?", new RowMapper<Student>() { @Override public Student mapRow(ResultSet rs, int i) throws SQLException { Student student = new Student(); //封装所有的属性 student.setId(rs.getInt("id")); student.setName(rs.getString("name")); student.setGender(rs.getBoolean("gender")); student.setBirthday(rs.getDate("birthday")); return student; } }, 1); System.out.println(student); } //使用RowMapper的实现类BeanPropertyRowMapper查询结果 @Test public void testQueryForObject2() { Student student = template.queryForObject("select * from student where id=?", new BeanPropertyRowMapper<>(Student.class), 2); System.out.println(student); }
- 查询多条记录封装成List<Map<String,Object>>
List<Map<String, Object>> queryForList(String sql, Object…args) 参数: 1) 要执行的SQL语句 2) 替换占位符的真实值 返回值: 一个封装好List<Map<String,Object>> 如果没有记录:返回List,没有元素。不会出现异常,也不为null。
练习
@Test public void testQueryForList() { List<Map<String, Object>> list = template.queryForList("SELECT * from student where gender=?", 1); //输出每一条记录 for (Map<String, Object> map : list) { System.out.println(map); } }
- 查询多条记录封装成List
List<T> query(String sql, RowMapper<T> rowMapper, Object... args) 参数: 1) 要执行的SQL语句 2) 行映射接口,传它的子类BeanPropertyRowMapper 3) 替换占位符的真实值 返回值:封装好的List集合,如果没有记录,集合中没有元素,不为NULL。
练习
@Test public void testQuery() { List<Student> students = template.query("select * from student where gender=?", new BeanPropertyRowMapper<>(Student.class), 1); for (Student student : students) { System.out.println(student); } }
- 查询1条记录:聚合函数
public <T> T queryForObject(String sql, Class<T> requiredType, Object... args) 参数: 1) 要执行的SQL语句 2) 指定返回的类型,如:int.class 3) 替换占位符的真实值 返回值: 指定类型的值
练习
@Test public void testQueryForObject3() { int count = template.queryForObject("select count(*) from student where gender=?", int.class, 1); System.out.println("男生人数:" + count); }