📄 实体对象的抽象以及一种基于数据库的实现(转).txt
字号:
PreparedStatement ps=_conn.prepareStatement ("insert into account(accountid,name) values(?,?)");
ps.setInt (1,accountId);
ps.setString (2,name);
ps.execute();
ps.close();
super.insert(); //注意,重载insert方法必须要有这一句
}
//重定义这个方法,执行实际的sql命令
public void update() throws SQLException {
PreparedStatement ps=_conn.prepareStatement ("update account set accountid=?, name=? Where accountid=?");
ps.setInt(1,accountId);
ps.setString(2,name);
ps.setInt(3,accountId);
ps.execute();
ps.close();
super.update(); //注意,重载update方法必须要有这一句
}
//重定义这个方法,执行实际的sql命令
public void delete() throws SQLException {
if (isDbStored()) {
PreparedStatement ps=_conn.prepareStatement("delete account where accountid = ? ");
ps.setInt(1,accountId);
ps.execute();
ps.close();
}
super.delete();
}
//下面这个方法把一个ResultSet转换成对象中的属性,下面所有的getByXXX方法都会用到这个方法。
public void _setAttribute(java.sql.ResultSet rs) throws SQLException {
accountId=rs.getInt(1);
name=rs.getString(2);
super._setAttribute(rs); //注意,重载_setAttribute方法必须要有这一句
}
//根据条件取得对象
public boolean getByAccountId(int id) throws SQLException {
clearDbStored();
PreparedStatement ps=_conn.prepareStatement ("select AccountId,Name from account where AccountId=?");
//这里的select语句得到的结果集必须与上面的_setAttribute方法假定的结果集一致
ps.setInt(1,id);
ResultSet rs=ps.executeQuery();
if (rs.next())
_setAttribute(rs);
rs.close();
ps.close();
return (isDbStored());
}
//根据条件取得对象并企图修改
public boolean getByAccountIdForUpdate(int id) throws SQLException {
clearDbStored();
PreparedStatement ps=_conn.prepareStatement ("select AccountId,Name from account where AccountId=? for update");
ps.setInt(1,id);
ResultSet rs=ps.executeQuery();
if (rs.next())
_setAttribute(rs);
rs.close();
ps.close();
return (isDbStored());
}
}
上面说到的对象中,只有getByXXX()这样的方法,这种方法只会返回唯一的对象,这时,也不用保存数据库结果集,如果出现返回一组对象的情况,就不能不保存结果集了,这时需要一个辅助类来实现。这个辅助类称为对象浏览器,下面定义对象浏览器。
import java.sql.*;
/**所有实体对象的浏览器的父类*/
abstract class EntityBrowser {
protected ResultSet _rs;
protected Connection _conn;
protected boolean _hasMoreElement;
/**浏览器是否还有下一个记录*/
public boolean hasMoreElement() throws SQLException{
return _hasMoreElement;
}
/**返回下一个记录,子类必须重载这个函数*/
abstract public Object nextElement() throws SQLException;
/**关闭这个浏览器*/
public void close () throws SQLException{
_rs.getStatement().close();
}
/**浏览器构造函数,指定一个数据库连接*/
public EntityBrowser(ResultSet rs) throws SQLException {
_rs=rs;
_conn=_rs.getStatement().getConnection();
_hasMoreElement=_rs.next();
}
//子类重载nextElement()时调用这个方法
protected Object _nextEntityObj(EntityObject eo) throws SQLException {
if (_hasMoreElement) {
eo._setAttribute(_rs);
} else eo=null;
_hasMoreElement=_rs.next();
return (eo);
}
}
有了EntityBrowser类后,我们来修改刚才定义的Account类。第一步是要在Account类里定义一个私有的EntityBrowser子类,这个子类只要定义一个构造函数并重载nextElement()方法即可。这里使用了在一个类里定义另一个类的技巧,这个技巧在java的容器类库中经常使用。第二步是在Account类中增加一个方法getAllByXX(),返回一组实体对象的方法名常用getAll打头。请看以下修改的代码片段。
import java.sql.*;
public class Account extends EntityObject {
。。。
/*以下是新修改的代码*/
//定义一个子类
private class AccountBrowser extends EntityBrowser {
AccountBrowser (ResultSet rs) throws SQLException { super(rs); }
Public Object nextElement() throws SQLException{
Account ac=new Account(_conn);
return (_nextEntityObj(ac));
}
}
//下面是实体对象的方法
public EntityBrowser getAll() throws SQLException {
PreparedStatement ps=_conn.prepareStatement("select AccountId,Name from account ");
ResultSet rs=ps.executeQuery();
return (new AccountBrowser(rs));
}
public EntityBrowser getAllByName(String name) throws SQLException {
PreparedStatement ps=_conn.prepareStatement(“select AccountId,Name from account where name like ?”;
ps.setString(1,name);
ResultSet rs=ps.executeQuery();
return (new AccountBrowser(rs));
}
接下来,讨论一下数据表的连接关系,关系数据库主要有一对一,一对多,多对多,多对一的关系。比如教师和学生之间的教课关系,这是一种多对多的关系。有的关系是有自己的属性,比如教课时间,我们可以把所有的关系都看作一个实体对象。但是为了简化程序,对于那些没有自己属性的一对一,一对多关系我们可以把它简化成父、子表的关系。表示数据库中的主表和子表有两种方法。
一种方法把子表和主表看作一个实体对象,子表用Vector或HashTable这样的Collection定义成主表的一个属性,在_setAttribute方法中,再执行一次sql查询,把查询出的子表数据放入collection中。同时还要修改insert,update方法,使得主表的实体对象每次数据插入或修改时都要删除并重新插入子表的数据。这里实际上把子表看作主表的一种附属数据结构,而不是独立的对象,子表的数据库操作全部由主表来完成。
另一种方法是把子表看作一个单独的实体类,实际上这时不存在主表和子表的概念了。只有两个实体对象,他们之间是一种关联的关系。主表通过getXXByXX()的方法来返回一个子表类的Browser。可以通过定义主表的一个方法insertXX(), 调用子表的insert()来插入一个新的子表项目;定义deleteXXX()来删除子表项目。至于更新子表数据,可以直接调用子表实体类的update()方法,不需要使用主表类的任何方法。
对于一个视图,也可以参照定义实体类的方法定义一个视图类,不过要尽量少用视图类,因为视图类和其他实体对象虽然语法上看不出关联,但语义上实际上是有关联的。一个实体类的修改常常要修改所有相关的视图类,这对于数据封装是很不利的。只有出于性能的考虑我们才使用它。
最后,探讨一下数据库事务的概念。事务是建立在数据库连接的基础上的,可以一次提交或回滚一系列操作。要实现事务,必须把数据库连接的自动提交属性设为false。java缺省的连接都是自动提交的,实现事务必须强制执行一个conn.setAutoCommit(false)。如果连接不是自动提交的,那么要注意,每当一个事务完成时必须执行commit或rollback,就算是select语句也必须提交事务。而且事务最好能尽早提交,比如每次select后提交,这样可以减少数据库资源的占用。
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -