分享
 
 
 

Torque中的一些问题

王朝java/jsp·作者佚名  2006-01-31
窄屏简体版  字體: |||超大  

Torque中的一些问题

说明:这里描述的问题来自: Torque3.1.1, Village2.0 dev,文中用到的代码来自开源Apache项目. 文中虽例子,但主要是辅助说明错误原因和解决办法.以给使用Turbine+Torque+Velocity的人一点帮助.

Torque是apache数据库项目中一个子项目,它实现数据库的持久层,是JDO的Apache实现.自从和Turbine项目分出来后,它形成了两部分:

生成器和运行时库.生成器用来生成数据库资源和访问数据库的类,使用这些类和在运行时库的支撑下,应用程序对数据库的各种操作得以用java对象的普通用法和操作习惯实现.

Torque除了用到Apache其他项目如Commons的成果外,还用到了一个开源项目:Village(http://share.whichever.com/index.php?SCREEN=village),它位于JDBC API上,提供不用写sql语句就可以实现数据库的crud操作.使用它和数据库里的表以及里面的记录打交道,就象是在一个小镇,一些小屋和小屋里的人们交谈,非常祥和的感觉.它的设计也是非常简洁,占用的空间也不大,真的就象一个游者,可以很轻松的驴行天下.

Torque的生成器使用了Velocity项目,该项目是java的模板语言,在生成数据库资源时,它出了全力,我们可以通过修改{Torque.dir}\templates中各子文件夹里的vm模板来适合数据库及你项目中的需要.例如,对于PostgreSQL8,Torque生成的库资源中创建数据库的sql代码中没有指定代码页,这样PostgreSQL将使用默认的SQL_ASCII设置,如果想让生成的资源指定代码页可以修改{Torque.dir}\templates\sql\db-init\postgresql中的createdb.vm,本例指定代码页为UNICODE,如下:

#foreach ($databaseName in $databaseNames)

drop database $databaseName;

create database $databaseName

ENCODING = 'UNICODE';#增加该行即可

#end

用Torque做为项目的持久层,其开发过程是很简单的,需要提供的总文件数是4个:

在设计阶段使用的build.properties,project-schema.xml,id-table-schema.xml以及在开发阶段的运行时用到的Torque.properties.在构建阶段为每个表产生五个类文件:

<table-name>.java,<table-name>Peer.java,

Base<table-name>.java,Base<table-name>Peer.java,这俩文件不要修改,因为用ant重新构建时,这些文件会重新生成.把文件名称以Peer结尾的称为Peer类,实现对象关系影射(ORM),不以Peer结尾的称为数据对象,应用和数据对象交互.不以base开头的类扩展了以base开头的类.业务逻辑应该加在子类上.<table-name>MapBuilder.java,这是用于特定表<table-name>的影射文件.Peer类的crud操作就是建立在Village之上的.

现在开始一个例子,项目名称是cms,以这个例子说明一些问题.

这是一个cms项目的模式文件cms-schema.xml:

<?xml version="1.0" encoding="gb2312" standalone="no" ?>

<!DOCTYPE database SYSTEM

"http://jakarta.apache.org/turbine/dtd/database.dtd">

<database name="news" package="org.news.om">

<table name="atts" idMethod="none" description="保存了新闻的附件信息,它和新闻存在引用关系.">

<column name="pagedate" javaName="pagedate" primaryKey="true" required="true" type="VARCHAR"

size="8" javaNamingMethod="underscore" description="页创建日期,缩略形式"></column>

<column name="pagecode" javaName="pagecode" primaryKey="true" required="true" type="VARCHAR"

size="5" javaNamingMethod="underscore" description="页代码"></column>

<column name="filename" javaName="filename" primaryKey="false" required="true" type="VARCHAR"

size="30" javaNamingMethod="underscore" description="文件名称"></column>

<column name="oldfilename" javaName="oldfilename" primaryKey="false" required="false" type="VARCHAR"

size="30" javaNamingMethod="underscore" description="旧文件名称"></column>

<column name="attname" javaName="attname" primaryKey="false" required="true" type="VARCHAR"

size="50" javaNamingMethod="underscore" description="附件名称,由用户指定的名称"></column>

<column name="img" javaName="img" primaryKey="false" required="false" type="BLOB"

javaNamingMethod="underscore" description="附件文件,保存大文件"></column>

<column name="attdes" javaName="attdes" primaryKey="false" required="false" type="VARCHAR"

size="50" javaNamingMethod="underscore" description="对附件的描述"></column>

</table>

</database>

我们只关注生成java文件,使用该文件Torque生成了5个类,下面是影射类AttsMapBuilder:

其代码片段为:

public void doBuild() throws TorqueException

{

dbMap = Torque.getDatabaseMap("news");

dbMap.addTable("atts");

TableMap tMap = dbMap.getTable("atts");

tMap.setPrimaryKeyMethod("none");

tMap.addPrimaryKey("atts.PAGEDATE", new String());

tMap.addPrimaryKey("atts.PAGECODE", new String());

tMap.addColumn("atts.FILENAME", new String());

tMap.addColumn("atts.OLDFILENAME", new String());

tMap.addColumn("atts.ATTNAME", new String());

tMap.addColumn("atts.IMG", new Object());

tMap.addColumn("atts.ATTDES", new String());

}

问题一:删除数据时报告错误:You must specify KeyDef attributes for this TableDataSet in order to create a Record for update.

原因:因为Torque的Peer使用了Village,而KeyDef是其中一个类,该类定义为:

public class KeyDef

{

private Vector mySelf = null;

public KeyDef()

{

mySelf = new Vector();

mySelf.addElement ("");

}

public KeyDef addAttrib(String name)

{

mySelf.addElement (name);

return this;

}

public boolean containsAttrib (String name)

{

return (mySelf.indexOf ((Object) name) == -1) ? false : true;

}

public String getAttrib (int pos)

{

if (pos == 0)

pos = 1;

try

{

return (String) mySelf.elementAt (pos);

}

catch (ArrayIndexOutOfBoundsException e)

{

return null;

}

}

public int size()

{

return mySelf.size() - 1;

}

}

该类的作用很简单,就是保存一个列名称.提供给其他类DataSet和Record使用,通过读其源码,发现Village很依赖表的primary键,如果一个表没有

primary键,那么你不能更新和删除记录.

解决办法就是为表创建primary键.

问题二:当删除多行时,遭到操作失败.

原因:Village实现的比较奇怪,就是插入更新删除记录时,若影响行数大于1时会生成一个异常,下面的代码片段位于Record类中:

private int saveWithDelete (Connection connection)

throws DataSetException, SQLException

{

PreparedStatement stmt = null;

try

{

stmt = connection.prepareStatement (getSaveString());

int ps = 1;

for (int i = 1; i <= dataset().keydef().size(); i++)

{

Value val = getValue (dataset().keydef().getAttrib(i));

val.setPreparedStatementValue (stmt, ps++);

}

int ret = stmt.executeUpdate();

setSaveType (Enums.ZOMBIE);

if (ret > 1)

throw new SQLException ("There were " + ret + " rows deleted with this records key value.");

return ret;

}

catch (SQLException e1)

{

throw e1;

}

finally { try

{

if (stmt != null)

stmt.close();

}

catch (SQLException e2)

{

throw e2;

}

}

}

而这个异常在org.apache.torque.util.BasePeer类中是这样处理的:

public static void doDelete(Criteria criteria) throws TorqueException

{

Connection con = null;

try

{

con = Transaction.beginOptional(

criteria.getDbName(),

criteria.isUseTransaction());

doDelete(criteria, con);

Transaction.commit(con);

}

catch (TorqueException e)

{

Transaction.safeRollback(con);

throw e;

}

}

事务回滚导致删除操作失败.实际上不仅是对删除操作这样,而且对于其它操作插入更新也是这样的.

解决办法就是重写Peer子类:因为Peer类是BasePeer的子类,但doDelete(Criteria criteria)是静态方法,不能覆盖,所以我们要重新命名

上面方法为doDelete1(Criteria criteria),在问题三具体给出代码.

问题三:有时候表中不存在唯一标识一条记录的字段组合(除了整行记录),想以某几个字段删除行.比如本例,

允许指定两个字段PAGEDATE,PAGECODE的值删除记录.

有两种办法:

1 为了能使字段PAGEDATE,PAGECODE的值相等的多行存在,手工删除表中的primary键,保持影射文件不变,增删改都可工作.

2 修改本例的BaseAttsPeer类,这样做要注意备份这个文件.

增加两个函数:doDelete1(Criteria criteria, Connection con),doDelete1(Criteria criteria),这俩函数替代BasePeer中的

doDelete(Criteria criteria, Connection con),doDelete(Criteria criteria)两个函数的功能,再把BaseAttsPeer类中定义的

doDelete所有重载版本的调用改到对doDelete1的调用,就可以了.

doDelete1代码片段为:

public static void doDelete1(Criteria criteria) throws TorqueException

{

Connection con = null;

try

{

con = Transaction.beginOptional(

criteria.getDbName(),true);

//criteria.isUseTransaction());

doDelete(criteria, con);

Transaction.commit(con);

}

catch (TorqueException e)

{

Transaction.commit(con);

//Transaction.safeRollback(con);

throw e;

}

}

/**

* Method to perform deletes based on values and keys in a Criteria.

*

* @param criteria The criteria to use.

* @param con A Connection.

* @throws TorqueException Any exceptions caught during processing will be

* rethrown wrapped into a TorqueException.

*/

public static void doDelete1(Criteria criteria, Connection con)

throws TorqueException

{

DB db = Torque.getDB(criteria.getDbName());

DatabaseMap dbMap = Torque.getDatabaseMap(criteria.getDbName());

// Set up a list of required tables and add extra entries to

// criteria if directed to delete all related records.

// StringStack.add() only adds element if it is unique.

HashSet tables = new HashSet();

Iterator it = criteria.keySet().iterator();

while (it.hasNext())

{

String key = (String) it.next();

Criteria.Criterion c = criteria.getCriterion(key);

List tableNames = c.getAllTables();

for (int i = 0; i < tableNames.size(); i++)

{

String name = (String) tableNames.get(i);

String tableName2 = criteria.getTableForAlias(name);

if (tableName2 != null)

{

tables.add(new StringBuffer(

name.length() + tableName2.length() + 1)

.append(tableName2)

.append(' ')

.append(name)

.toString());

}

else

{

tables.add(name);

}

}

if (criteria.isCascade())

{

// This steps thru all the columns in the database.

TableMap[] tableMaps = dbMap.getTables();

for (int i = 0; i < tableMaps.length; i++)

{

ColumnMap[] columnMaps = tableMaps[i].getColumns();

for (int j = 0; j < columnMaps.length; j++)

{

// Only delete rows where the foreign key is

// also a primary key. Other rows need

// updating, but that is not implemented.

if (columnMaps[j].isForeignKey()

&& columnMaps[j].isPrimaryKey()

&& key.equals(columnMaps[j].getRelatedName()))

{

tables.add(tableMaps[i].getName());

criteria.add(columnMaps[j].getFullyQualifiedName(),

criteria.getValue(key));

}

}

}

}

}

Iterator tabIt = tables.iterator();

while (tabIt.hasNext())

{

String tab = (String) tabIt.next();

KeyDef kd = new KeyDef();

HashSet whereClause = new HashSet();

ColumnMap[] columnMaps = dbMap.getTable(tab).getColumns();

for (int j = 0; j < columnMaps.length; j++)

{

ColumnMap colMap = columnMaps[j];

/*原来的在BasePeer类中操作KeyDef加入列字段的代码

if (colMap.isPrimaryKey())

{

kd.addAttrib(colMap.getColumnName());

}

*/

String key = new StringBuffer(colMap.getTableName())

.append('.')

.append(colMap.getColumnName())

.toString();

if (criteria.containsKey(key))

{

if (criteria.getComparison(key).equals(Criteria.CUSTOM))

{

whereClause.add(criteria.getString(key));

}

else

{

//增加下列语句,是为了提供where子句中的限定,以利于删除数据.

//这种修改就避免了表一定要有primarykey的限制.

kd.addAttrib(colMap.getColumnName());

whereClause.add(SqlExpression.build(

colMap.getColumnName(),

criteria.getValue(key),

criteria.getComparison(key),

criteria.isIgnoreCase(),

db));

}

}

}

// Execute the statement.

TableDataSet tds = null;

try

{

tds = new TableDataSet(con, tab, kd);

String sqlSnippet = StringUtils.join(whereClause.iterator(), " AND ");

if (log.isDebugEnabled())

{

log.debug("BasePeer.doDelete: whereClause=" + sqlSnippet);

}

tds.where(sqlSnippet);

tds.fetchRecords();

if (tds.size() > 1 && criteria.isSingleRecord())

{

handleMultipleRecords(tds);

}

for (int j = 0; j < tds.size(); j++)

{

Record rec = tds.getRecord(j);

rec.markToBeDeleted();

rec.save();

}

}

catch (Exception e)

{

throwTorqueException(e);

}

finally

{

if (tds != null)

{

try

{

tds.close();

}

catch (Exception ignored)

{

}

}

}

}

}

private static void throwTorqueException(Exception e) throws TorqueException {

if (e instanceof TorqueException) {

throw (TorqueException) e;

}

else {

throw new TorqueException(e);

}

}

这种修改解决了本问题,也彻底修正了上面遇到的2个问题.

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有