今日是JDBC的最后一天了,继续JDBC的高级应用。说是高级应用,只是针对JDBC的再次封装,使得使用JDBC变得更加简单。DBUtil就是这样的库!今日上午首先讲解JDBC操作多个实体之多对多关系,Hibnate正是使用了这样的手法,这一功能让人感觉很好。然后方老师将以前使用的JDBC操作中,有重复的代码提取出来,形成一个单独的类,大大简化了JDBC的操作。起初我还没有想到这就是DBUtil,若不是老方说:我们是不是还有DBUtil没讲?我们现在就已经做出来了…!呵呵,就这么简单!
开始复习、总结今日的主要内容!
一、JDBC操作多个实体——多对多
昨天有简单的介绍一对多的关系,使用的是部门表与员工表式之间的关系。下面举例使用,老师表与学生表之间的关系。
首先让我们设计一个老师表(老师能找到自己的学生):
ID
姓名
学生ID(外键)
1
张三峰
1
2
方世玉
2
1
张三峰
1
2
方世玉
2
…
再设计一个学生表(学生能找到自己的老师):
ID
姓名
教师ID(外键)
1
AAA
1
2
BBB
2
1
AAA
1
2
BBB
2
…
显而易见上边的两张表设计的有问题,比较“脏”一点也不优雅。我们得想一个办法解决它!
创建一个中间表:
教师_ID(外键)
学生_ID(外键)
1
1
2
2
1
2
2
1
“教师_ID”字段是外键,指向教师表中的ID。“学生_ID”是个外键,指向学生表中的ID。在某些情况下中间表还定义一些其他字段。
去除教师表中的“学生ID”和学生表中的“教师ID”,这样以后只要连表查询即可。
上面只是举个例子,在实际开发中应该先创建对象模型,然后再创建数据库关系模型。使用对象反应上边的关系模型,依然是两个对象。教师对象和学生对象,教师对象有一个SET成员,记录他的所有学生。学生对象包含一个SET记录他的所有教师。
创建多个学生对象(不需要设置SET成员),将他们添加到教师对象的SET中。将教师对象传递给DAO工具对象,DAO工具对象读取教师,将教师信息保存到教师表中。然后遍历所有学生,将学生信息保存到学生表中,同时将教师ID与学生ID保存到中间表中。OK,这样就完成了!
这就是多对多关系——建立两个一对多关系!
再次注意:要尽量使用多对一关系,而避免使用一对多或多对多关系!
二、简化JDBC操作——DBUtil实现原理和元数据
还记得我们以前写的操作数据库记录的DOMAIN类吗?回想一下,插入记录、修改记录、查询记录、删除记录。在这些方法中,我们都需要获取数据库连接,对数据记录操作完成后,还需要释放这些记录。没感觉到什么不对吗?当然感觉到了,重复的代码。既然有重复的代码就应该将它提取出来,实现代码的重用。OK,现在我们就把重复的代码提取出来。为了减小篇章,我们只提取插入记录(update)和查询记录。因为修改和查询与插入一样都是调用update语句。
MyDBUtil.java:
/**
* 封装JDBC操作中重复的代码,使JDBC操作变得简单。
* @author Administrator
*
*/
public class MyDBUtil {
// update可以执行 添加、修改、删除操作!
public void update(String sql, Object[] args) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JdbcUtil.getDataSource().getConnection();
// 将参数填充到sql语句中
ps = conn.prepareStatement(sql);
for (int i = 0; args != null && i < args.length; i++) {
ps.setObject(i + 1, args[i]);
}
ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 释放连接
JdbcUtil.release(conn, ps, rs);
}
}
// 查询操作
public Object find(String sql, Object[] args, ResultSetHandler rsh) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
Object obj = null;
try {
conn = JdbcUtil.getDataSource().getConnection();
// 将参数填充到sql语句中
ps = conn.prepareStatement(sql);
// 获取查询结果
rs = ps.executeQuery();
for (int i = 0; args != null && i < args.length; i++) {
ps.setObject(i + 1, args[i]);
}
// 调用Handler封装数据
obj = rsh.handler(rs);
} catch (Exception e) {
e.printStackTrace();
} finally {
// 释放连接
JdbcUtil.release(conn, ps, rs);
}
return obj;
}
}
ResultSetHandler.java:
/**
* Handler,使用策略模式封装ResultSet中的数据到Bean中。
* @author Administrator
*
*/
interface ResultSetHandler{
Object handler(ResultSet rs);
}
BeanHandler.java:
/**
* Handler,实现类
* @author Administrator
*
*/
class BeanHandler implements ResultSetHandler{
Class clazz;
public BeanHandler(Class clazz){
this.clazz = clazz;
}
public Object handler(ResultSet rs) {
Object obj = null;
try {
// 注意此处使用的ResultSetMetaData,元数据
ResultSetMetaData rsd;
rsd = rs.getMetaData();
obj = this.clazz.newInstance();
// 将读取的记录设置到Bean对象中
for (int i = 1; i <= rsd.getColumnCount(); i++) {
// 获取字段名
String cln = rsd.getColumnName(i);
// 获取成员
Field field = this.clazz.getDeclaredField(cln);
// 设置成员值
field.setAccessible(true);
field.set(obj, rs.getObject(i));
}
} catch (Exception e) {
e.printStackTrace();
}
return obj;
}
}
上面只是简单的对JDBC的重复代码进行了封装,并使用策略模式对find操作返回值进行了与具体Bean的分离,十分优雅。DBUtil工具包,就是使用上面的原理实现的!
注意:Handler实现类中的“ResultSetMetaData”,它被称为元数据。JDBC的元数据有:
1. Connection.getDataBaseMetaData(),返回DataBaseMetaData,它包含的方法有:
getURL():返回一个String类对象,代表数据库的URL。
getUserName():返回连接当前数据库管理系统的用户名。
getDatabaseProductName():返回数据库的产品名称。
getDatabaseProductVersion():返回数据库的版本号。
getDriverName():返回驱动驱动程序的名称。
getDriverVersion():返回驱动程序的版本号。
isReadOnly():返回一个boolean值,指示数据库是否只允许读操作。
老方说有的框架会使用到它,来获取数据库的连接等信息。具体在框架中如何应用,等到学习框架时再深入吧!
2. PreparedStatement.getParameterMetaData(),返回ParameterMetaData,它包含的方法:
getParameterCount():获得指定参数的个数
getParameterType(int param):获得指定参数的sql类型
在上面的例子中有用到过,在DBUtil中有使用getParameterType获取数据类型填充空的Statement参数。
3. ResultSet.getMetaData(),返回ResultSetMetaData,它包含的方法:
getColumnCount():返回resultset对象的列数
getColumnName(int column):获得指定列的名称
getColumnTypeName(int column):获得指定列的类型
在上面的例子中我们有使用ResultSetMetaData。
O-R Mapping(Object – Relation Mapping)映射工具,对象模型与关系模型的映射:Hibernate、Ibatis、DBUtil。
DBUtil在此就不多做介绍了,学了上边的实现原理,自己也可以编写一个DBUtil出来。DBUtil的具体使用方式可以查看它的文档!
三、XML Schema
今日的课程的补充,咱们的Servlet的web.xml文件正是使用Schema描述的。以后的框架中也会使用它。Schema已经成为w3c组织的标准,并逐步取代DTD。Schema要比DTD复杂的多,因为它引入了命名空间,多数据类型这些操作。Schema也是个XML文件!
今日就简单学习一下Schema,不深入介绍了,其实它也没什么。
1. 定义Schema约束文件:
<?xml version="1.0" encoding="UTF-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.changcheng.org"
elementFormDefault="qualified">
<xs:element>
<xs:complexType>
<!-- 要求元素必须按照下边的顺序排列,没有数量限制 -->
<xs:sequence maxOccurs='unbounded' >
<!-- 根元素 -->
<xs:element name='根元素名称' >
<!-- 可添加多个子元素 -->
<xs:complexType>
<!-- 要求元素必须按照下边的顺序排列 -->
<xs:sequence>
<!-- 子元素可以定义自己的多个子元素 -->
<xs:element name='子元素名称' type='xs:元素类型' />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:schema>
其中“targetNamespace”定义自己编写的Schema的命名空间。
2. 在XML引入并使用Schema约束:
<cc:根元素 xmlns:cc="http://www.changcheng.org"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.changcheng.org book.xsd">
<cc:子元素>元素值</cc:子元素>
</cc:根元素>
其中“xsi:schemaLocation”指定引入的Schema文件所在的位置。一个XML文件中可以引入多个Schema约束文件。同样在“xsi:schemaLocation”使用空格分隔,指定Schema约束文件的位置。“xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"”不需要指定位置,因为它是w3c组织定义的文件,Schema解析器,知道它的具体位置。
Schema文档通常称之为模式文档(约束文档),遵循这个文档书写的xml文件称之为实例文档。Schema具体使用方式参见w3c的文档吧!
OK,今天的课程内容总结到此结束!老方刚上课时就提到大家一起合影,但课上一直很忙。今天本要合影的,但也没抽出时间。大家一起定下后天中午合影。早就想跟大家一起合影了!
老方还有5天的课程就结束了JAVAWEB基础部分,由此步入高级应用的殿堂。大家学习兴致勃勃,一定要多练习啊!到后来的项目,一天写上千行代码都正常。高级应用,使用上非常简单,其实我们学习的基础部分,也为学习高级应用打下了坚实的基础。因为高级应用中那些框架正是使用这些基础知识实现的。
我来此学习还有一个大的目标就是,项目架构、文档的编写、项目的管理、UML…这些对我来说太重要了,我可不是新手哦!我对此十分期待!
加油!
开始复习、总结今日的主要内容!
一、JDBC操作多个实体——多对多
昨天有简单的介绍一对多的关系,使用的是部门表与员工表式之间的关系。下面举例使用,老师表与学生表之间的关系。
首先让我们设计一个老师表(老师能找到自己的学生):
ID
姓名
学生ID(外键)
1
张三峰
1
2
方世玉
2
1
张三峰
1
2
方世玉
2
…
再设计一个学生表(学生能找到自己的老师):
ID
姓名
教师ID(外键)
1
AAA
1
2
BBB
2
1
AAA
1
2
BBB
2
…
显而易见上边的两张表设计的有问题,比较“脏”一点也不优雅。我们得想一个办法解决它!
创建一个中间表:
教师_ID(外键)
学生_ID(外键)
1
1
2
2
1
2
2
1
“教师_ID”字段是外键,指向教师表中的ID。“学生_ID”是个外键,指向学生表中的ID。在某些情况下中间表还定义一些其他字段。
去除教师表中的“学生ID”和学生表中的“教师ID”,这样以后只要连表查询即可。
上面只是举个例子,在实际开发中应该先创建对象模型,然后再创建数据库关系模型。使用对象反应上边的关系模型,依然是两个对象。教师对象和学生对象,教师对象有一个SET成员,记录他的所有学生。学生对象包含一个SET记录他的所有教师。
创建多个学生对象(不需要设置SET成员),将他们添加到教师对象的SET中。将教师对象传递给DAO工具对象,DAO工具对象读取教师,将教师信息保存到教师表中。然后遍历所有学生,将学生信息保存到学生表中,同时将教师ID与学生ID保存到中间表中。OK,这样就完成了!
这就是多对多关系——建立两个一对多关系!
再次注意:要尽量使用多对一关系,而避免使用一对多或多对多关系!
二、简化JDBC操作——DBUtil实现原理和元数据
还记得我们以前写的操作数据库记录的DOMAIN类吗?回想一下,插入记录、修改记录、查询记录、删除记录。在这些方法中,我们都需要获取数据库连接,对数据记录操作完成后,还需要释放这些记录。没感觉到什么不对吗?当然感觉到了,重复的代码。既然有重复的代码就应该将它提取出来,实现代码的重用。OK,现在我们就把重复的代码提取出来。为了减小篇章,我们只提取插入记录(update)和查询记录。因为修改和查询与插入一样都是调用update语句。
MyDBUtil.java:
/**
* 封装JDBC操作中重复的代码,使JDBC操作变得简单。
* @author Administrator
*
*/
public class MyDBUtil {
// update可以执行 添加、修改、删除操作!
public void update(String sql, Object[] args) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JdbcUtil.getDataSource().getConnection();
// 将参数填充到sql语句中
ps = conn.prepareStatement(sql);
for (int i = 0; args != null && i < args.length; i++) {
ps.setObject(i + 1, args[i]);
}
ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 释放连接
JdbcUtil.release(conn, ps, rs);
}
}
// 查询操作
public Object find(String sql, Object[] args, ResultSetHandler rsh) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
Object obj = null;
try {
conn = JdbcUtil.getDataSource().getConnection();
// 将参数填充到sql语句中
ps = conn.prepareStatement(sql);
// 获取查询结果
rs = ps.executeQuery();
for (int i = 0; args != null && i < args.length; i++) {
ps.setObject(i + 1, args[i]);
}
// 调用Handler封装数据
obj = rsh.handler(rs);
} catch (Exception e) {
e.printStackTrace();
} finally {
// 释放连接
JdbcUtil.release(conn, ps, rs);
}
return obj;
}
}
ResultSetHandler.java:
/**
* Handler,使用策略模式封装ResultSet中的数据到Bean中。
* @author Administrator
*
*/
interface ResultSetHandler{
Object handler(ResultSet rs);
}
BeanHandler.java:
/**
* Handler,实现类
* @author Administrator
*
*/
class BeanHandler implements ResultSetHandler{
Class clazz;
public BeanHandler(Class clazz){
this.clazz = clazz;
}
public Object handler(ResultSet rs) {
Object obj = null;
try {
// 注意此处使用的ResultSetMetaData,元数据
ResultSetMetaData rsd;
rsd = rs.getMetaData();
obj = this.clazz.newInstance();
// 将读取的记录设置到Bean对象中
for (int i = 1; i <= rsd.getColumnCount(); i++) {
// 获取字段名
String cln = rsd.getColumnName(i);
// 获取成员
Field field = this.clazz.getDeclaredField(cln);
// 设置成员值
field.setAccessible(true);
field.set(obj, rs.getObject(i));
}
} catch (Exception e) {
e.printStackTrace();
}
return obj;
}
}
上面只是简单的对JDBC的重复代码进行了封装,并使用策略模式对find操作返回值进行了与具体Bean的分离,十分优雅。DBUtil工具包,就是使用上面的原理实现的!
注意:Handler实现类中的“ResultSetMetaData”,它被称为元数据。JDBC的元数据有:
1. Connection.getDataBaseMetaData(),返回DataBaseMetaData,它包含的方法有:
getURL():返回一个String类对象,代表数据库的URL。
getUserName():返回连接当前数据库管理系统的用户名。
getDatabaseProductName():返回数据库的产品名称。
getDatabaseProductVersion():返回数据库的版本号。
getDriverName():返回驱动驱动程序的名称。
getDriverVersion():返回驱动程序的版本号。
isReadOnly():返回一个boolean值,指示数据库是否只允许读操作。
老方说有的框架会使用到它,来获取数据库的连接等信息。具体在框架中如何应用,等到学习框架时再深入吧!
2. PreparedStatement.getParameterMetaData(),返回ParameterMetaData,它包含的方法:
getParameterCount():获得指定参数的个数
getParameterType(int param):获得指定参数的sql类型
在上面的例子中有用到过,在DBUtil中有使用getParameterType获取数据类型填充空的Statement参数。
3. ResultSet.getMetaData(),返回ResultSetMetaData,它包含的方法:
getColumnCount():返回resultset对象的列数
getColumnName(int column):获得指定列的名称
getColumnTypeName(int column):获得指定列的类型
在上面的例子中我们有使用ResultSetMetaData。
O-R Mapping(Object – Relation Mapping)映射工具,对象模型与关系模型的映射:Hibernate、Ibatis、DBUtil。
DBUtil在此就不多做介绍了,学了上边的实现原理,自己也可以编写一个DBUtil出来。DBUtil的具体使用方式可以查看它的文档!
三、XML Schema
今日的课程的补充,咱们的Servlet的web.xml文件正是使用Schema描述的。以后的框架中也会使用它。Schema已经成为w3c组织的标准,并逐步取代DTD。Schema要比DTD复杂的多,因为它引入了命名空间,多数据类型这些操作。Schema也是个XML文件!
今日就简单学习一下Schema,不深入介绍了,其实它也没什么。
1. 定义Schema约束文件:
<?xml version="1.0" encoding="UTF-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.changcheng.org"
elementFormDefault="qualified">
<xs:element>
<xs:complexType>
<!-- 要求元素必须按照下边的顺序排列,没有数量限制 -->
<xs:sequence maxOccurs='unbounded' >
<!-- 根元素 -->
<xs:element name='根元素名称' >
<!-- 可添加多个子元素 -->
<xs:complexType>
<!-- 要求元素必须按照下边的顺序排列 -->
<xs:sequence>
<!-- 子元素可以定义自己的多个子元素 -->
<xs:element name='子元素名称' type='xs:元素类型' />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:schema>
其中“targetNamespace”定义自己编写的Schema的命名空间。
2. 在XML引入并使用Schema约束:
<cc:根元素 xmlns:cc="http://www.changcheng.org"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.changcheng.org book.xsd">
<cc:子元素>元素值</cc:子元素>
</cc:根元素>
其中“xsi:schemaLocation”指定引入的Schema文件所在的位置。一个XML文件中可以引入多个Schema约束文件。同样在“xsi:schemaLocation”使用空格分隔,指定Schema约束文件的位置。“xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"”不需要指定位置,因为它是w3c组织定义的文件,Schema解析器,知道它的具体位置。
Schema文档通常称之为模式文档(约束文档),遵循这个文档书写的xml文件称之为实例文档。Schema具体使用方式参见w3c的文档吧!
OK,今天的课程内容总结到此结束!老方刚上课时就提到大家一起合影,但课上一直很忙。今天本要合影的,但也没抽出时间。大家一起定下后天中午合影。早就想跟大家一起合影了!
老方还有5天的课程就结束了JAVAWEB基础部分,由此步入高级应用的殿堂。大家学习兴致勃勃,一定要多练习啊!到后来的项目,一天写上千行代码都正常。高级应用,使用上非常简单,其实我们学习的基础部分,也为学习高级应用打下了坚实的基础。因为高级应用中那些框架正是使用这些基础知识实现的。
我来此学习还有一个大的目标就是,项目架构、文档的编写、项目的管理、UML…这些对我来说太重要了,我可不是新手哦!我对此十分期待!
加油!