EasyDBO上对象关联还是存在很大问题。
其中一个,添加对象时,没能将被关联对象的主键(由数据库生成)插入到关联对象的外键上。
注:
(1) EasyDBO的一对一关联中,外键在关联对象方。
Class A{
.....
B b ;
}
Class B{
}
对应于实体A的数据库表有外键,如 fk_b
(2) EasyDBO的一对多关联中,外键在被关联对象方(多方)。
Class A{
.....
List<B> bList ;
}
Class B{
A a;
}
对应于实体B的数据库表有外键,如 fk_a
在解决这个问题中,对源码做一些的修改:
1、首先解决如果是数据库生成的主键(如auto_increment类型),在DatabaseDAO添加getGeneratedKey()方法。
{
if(prepared==null){
return null;
}
Serializable ret =null;
ResultSet rs = null;
try
{
rs = prepared.getGeneratedKeys();
if(rs.next())
{
ret = (Serializable)rs.getObject(1);
logger.info("get the GeneratedKey:" + ret);
}
}
catch(SQLException e)
{
e.printStackTrace();
}
finally
{
if(rs != null)
try
{
rs.close();
}
catch(SQLException e)
{
e.printStackTrace();
}
}
return ret;
}
2、在EasyJDBEngine中添加generatedKey字段和getter方法。
public Serializable getGeneratedKey()
{
return generatedKey;
}
在EasyJDBEngine的public boolean add(DBObject obj)方法中try的括号末尾添加
generatedKey = dba.getGeneratedKey();
public boolean add(DBObject obj) { // 添加一个对象
...
try {
......
generatedKey = dba.getGeneratedKey();
} catch (Exception e) {
3、EasyJDB中添加以下方法:
BeanWrapper wrapper = new BeanWrapper(obj);
DBTable dbTable = findTable(obj.getClass());
String idFieldName = dbTable.getPoperty(dbTable.getId());
if(wrapper.getPropertyValue(idFieldName)==null && StringUtils.isNotEmpty(dbEngine.getGeneratedKey())){
Object javaPropertyValue = wrapper.convertIfNecessary(String.valueOf(dbEngine.getGeneratedKey()),
dbTable.getType(idFieldName));
wrapper.setPropertyValue(idFieldName, javaPropertyValue);
System.out.println("setting id.");
return javaPropertyValue;
}
return null;
}
private Object getObjectIdValue(Object obj) {
BeanWrapper wrapper = new BeanWrapper(obj);
DBTable dbTable = findTable(obj.getClass());
String idFieldName = dbTable.getPoperty(dbTable.getId());
if(StringUtils.isNotEmpty(wrapper.getPropertyValue(idFieldName))){
return wrapper.getPropertyValue(idFieldName);
}
return null;
}
private void setClassFieldValue(Object target,ClassField classField,Object value) {
BeanWrapper wi=new BeanWrapper(target);
DBTable dbTable = findTable(target.getClass());
wi.setPropertyValue(dbTable.getPoperty(classField.getColumn()),value);
}
EasyJDB中的public boolean add(Object obj)方法改为:
{
logger.debug("把对象obj保存到数据库中");
boolean ret = dbEngine.add(obj2dbo(obj));
setObjectGenIdValue(obj);///如果是新添加的记录,且主键为数据库生成,那么将主键值返回赋给对象
if(ret)ret = ret & addRelativeObject(obj);
return ret;
}
EasyJDB中的private boolean addRelativeObject(Object obj)方法改为:
{
boolean ret = true;
DBTable table = findTable(obj.getClass());
if(table != null)
{
BeanWrapper wrapper = new BeanWrapper(obj);
java.util.Iterator it = table.getClassField().entrySet().iterator();
while(it.hasNext())
{
// 尝试处理其它字段
Map.Entry en = (Map.Entry)it.next();
String propertyName = (String)en.getKey();
ClassField classField = (ClassField)en.getValue();
// System.out.println(classField.getClass()+":"+propertyName);
if(classField instanceof ManyToManyField && wrapper.isReadableProperty(propertyName))
{
// 处理多对多
Object value = wrapper.getPropertyValue(propertyName);
// System.out.println("值内容"+value.getClass());
if(value != null && value instanceof Collection)
{
java.util.Iterator es = ((Collection)value).iterator();
while(es.hasNext())
{
Object element = es.next();
ret = ret & this.saveOrUpdate(element);
// 保存关联的第三方表
ManyToManyField field = (ManyToManyField)classField;
/**//*
* DBTable table2=findTable(element.getClass());
* if(table2!=null) { ManyToManyField field2=null;
* java.util.Iterator
* inf2=table2.getClassField().values().iterator();
* while(inf2.hasNext()) { ClassField
* ff=(ClassField)inf2.next();
* if(ff.getTableName().equals(field.getTableName()) &&
* ff.getType()==obj.getClass()) {
* field2=(ManyToManyField)ff; } }
*/
String sql = "insert into " + field.getTableName() + "(" + classField.getColumn() + ","
+ field.getTagColumn() + ") values(?,?)";
java.util.Collection paras = new java.util.ArrayList();
paras.add(wrapper.getPropertyValue(classField.getKey()));
BeanWrapper wrapper2 = new BeanWrapper(element);
paras.add(wrapper2.getPropertyValue(field.getTagKey()));
try
{
this.execute(sql, paras);
}
catch(Exception e)
{
logger.error("插入多对多关系时出错!");
e.printStackTrace();
}
// }
}
}
}
else if((classField instanceof ManyToOneField) && wrapper.isReadableProperty(propertyName))
{
// 处理一对多
Object value = wrapper.getPropertyValue(propertyName);
if(value != null && value instanceof Collection)
{
java.util.Iterator es = ((Collection)value).iterator();
while(es.hasNext())
{
Object item = es.next();
setClassFieldValue(item,classField,obj);//设置关联值
ret = ret & this.saveOrUpdate(item);
}
}
}
else if((classField instanceof OneToOneField) && wrapper.isReadableProperty(propertyName))
{
// 处理一对一
Object value = wrapper.getPropertyValue(propertyName);
if(value != null)
{
ret = ret & this.saveOrUpdate(value);
if(StringUtils.isNotEmpty(getObjectIdValue(value)))
{
setClassFieldValue(obj, classField, value);//设置关联值
update(obj);//更新一下数据,将关联的对象主键作为外键
}
}
}
}
}
return ret;
}
EasyJDB中的public boolean update(Object obj)方法改为:
{
logger.info("更新持久层中的数据表对象");
boolean ret = dbEngine.update(obj2dbo(obj));
// ret = ret & this.addRelativeObject(obj);//这句很容易会导致死循环,先不要了
return ret;
}
EasyJDB中的public boolean saveOrUpdate(Object obj)方法改为:
{
// logger.info("把对象持久化到数据库中,先偿试添加,若无法保存则执行修改操作");
boolean ret = false;
try
{
Object id = getObjectIdValue(obj);
if(id == null)
{
ret = add(obj);
}
else
{
if(get(obj.getClass(), id) != null)
ret = update(obj);
else
ret = add(obj);
}
}
catch(Exception e)
{
//这里的也先不要的,很容易导致死循环
// 出错,进一步尝试使用原始的方法进行更改
// if(add(obj))
// {
// return true;
// }
// return update(obj);
e.printStackTrace();
}
return ret;
}
解释一下:
这里处理的不包括多对多的关系,很少用到,暂时不管。
解决的思路是,如果是自增(数据库生成的id),那么获取这个id,并回赋给对象,对象有了主键值(每个这样的对象都回赋主键值),关联就有了导航。
在一对一关系处理中,因为是先插入关联对象数据,然后在插入被关联对象数据。但是关联对象需要知道被关联的主键值,而被关联的主键值是在最后插入数据后回赋的,所以,采取从新将对象关联,并更新数据。
setClassFieldValue(obj, classField, value);//设置关联值
update(obj);//更新一下数据,将关联的对象主键作为外键
在处理一对多(也即多对一)关系中,如一开就提到的“EasyDBO的一对多关联中,外键在被关联对象方(多方)”,数据插入顺序同一对一的一样,先关联对象,后被关联对象,所以,在这里只需要在被关联对象插入前更新关联就可以了
setClassFieldValue(item,classField,obj);//设置关联值
比较明显,将一对多的关系转换成了多个一对一了。或许这样不是很好,但是,在原有的架构上,比较难做更大修改。
EasyDBO有一些主键生成器的,不知对对象关联上主键的处理上有没有独特的解决方法,待探索...
浅见,不当的地方欢迎指正~~~~~~~~