高效地执行具有多个相似类的 CRUD 操作?
Posted
技术标签:
【中文标题】高效地执行具有多个相似类的 CRUD 操作?【英文标题】:Efficiently performing CRUD operations with multiple similar classes? 【发布时间】:2012-12-10 13:36:50 【问题描述】:我有一个抽象类,我们称之为Vehicle。 Diesel、Electric 和 Gas 类扩展了 Vehicle。这 3 个类有自己的方法和变量,以及它们从 Vehicle 继承的内容。
我想将 Diesel、Electric 和 Gas 对象中的变量放入 同一张表中一个数据库。从 Electric 对象创建的记录只会有几个空列,其中没有与 Diesel 对象相同的变量,等等。
我想创建插入行、更新行和检索行的方法,所有这些方法都接受来自这些类的对象作为参数。
我正在寻找这个问题的最佳解决方案,因为我认为我不知道它是什么。到目前为止,我能想到一些解决方案,但我不喜欢其中任何一个:
创建以下每个方法中的 4 个: 插入、更新、删除和检索/选择。这会导致很多重复代码。
public void AddVehicle(Diesel diesel)
public void AddVehicle(Electric electric)
public void AddVehicle(Gas gas)
public Electric ViewVehicles()
public Diesel ViewVehicle()
public Gas ViewVehicle()
public void RemoveVehicle(Diesel diesel)
public void RemoveVehicle(Electric electric)
public void RemoveVehicle(Gas gas)
public void UpdateVehicle(Diesel diesel)
public void UpdateVehicle(Electric electric)
public void UpdateVehicle(Gas gas)
创建每个方法以便它可以接受任何对象:(而不是采用 any 对象,有没有办法指定它可以采用的对象列表,但仍然只取其中一个?即。public void AddVehicle(柴油||电动||燃气))
public void AddVehicle(Object vehicle)
public Object ViewVehicle(Object vehicle)
public void RemoveVehicle(Object vehicle)
public void UpdateVehicle(Object vehicle)
创建一个“主”类,该类可以包含任何类(Diesel、Electric 或 Gas),然后将其传递给 insert/update/view/delete 方法。 (将插入/更新/查看方法设置为接受此“主”类作为参数。)“主”类的构造函数不完整:
public MasterVehicle(Diesel diesel)
public MasterVehicle(Electric electric)
public MasterVehicle(Gas gas)
其他一些适当、干净且更有意义的解决方案。
但是#4 可能是什么?
【问题讨论】:
我在描述中犯了一个小但巨大的错误,排除了一个简单的解决方案。类extend Vehicle 而不是implementing Vehicle。我之前曾写道,他们实施了车辆。道歉。 对许多类中的大部分重复代码使用public static PreparedStatement prepareVehicleStatement(Vehicle vehicle)/*code*/
可以避免大部分代码重复,但这并不是问题的全面解决方案。
@Lemming19 用英文实现我认为适合描述你的场景。在java中,关键字implements
用于不同的目的,但从语义的角度来看,我发现你的句子是正确的。所以不用担心;您的能力和沟通技巧毫无疑问。
【参考方案1】:
关于您的选择两个: 首先,您可以使用 Vehicle 而不是 Object,只要 Diesel、Electric 和 Gas 实施/扩展车辆。 它应该可以完成这项工作。
另外对于选项二中的 ViewVehicle :
public Object ViewVehicle(对象车辆)
您可以使用泛型方法,以便它始终返回与它进入的类型相同的类型。 更多信息在这里:http://docs.oracle.com/javase/tutorial/extra/generics/methods.html ...尽管我不清楚您为什么尝试将对象发送到然后从该方法返回的方法。
【讨论】:
我不小心写到类实现了 Vehicle 而不是扩展它。随着他们扩展车辆,我不相信我可以简单地通过车辆。扩展类有自己的方法和变量,而 Vehicle 没有。当我尝试引用 Diesel 内部而不是 Vehicle 内部的变量时,这是一个问题。 只是为了确认而尝试,尝试public static <T extends Vehicle> Long addVehicle(T vehicle)
会产生与 addVehicle(Vehicle vehicle) 相同的问题;调用 Vehicle 中不存在的变量或方法不起作用。但是,当尝试通过为每个 CRUD 操作提供额外的 Vehicle
方法来删除某些代码重复时,使用泛型确实有效。然后,使用那个Vehicle
方法来覆盖每个方法中之前重复的一些代码。【参考方案2】:
Generic DAO 是您可能感兴趣的内容。尽量避免编写冗长的答案,所以这里是链接。 j-genericdao
您可以在互联网上找到很多通用 dao 的变体。
【讨论】:
【参考方案3】:幸运的是,其他人也不得不处理这个问题:)
这里实际上有两个问题。一个是您确定的 API 的设计,另一个是如何处理对 DB 的实际存储/读取。
至于第一个问题,我会选择您的选项 #2,因为它最好遵循 OO 设计原则。但我不会让该方法接受Object
。相反,让它接受Vehicle
类型。
关于第二个问题,你可以看看下面的链接:Entity Inheritance Mapping Strategies。即使您不打算使用 JPA(或任何 ORM 框架),这些也可能是您想要了解的策略。
这里对上面链接中提到的单表策略进行了更详细的解释。这应该会给你一个好主意 --> Inheritance: Single Table Strategy
【讨论】:
我在描述中犯了一个小但巨大的错误,(我相信)排除了使方法接受车辆而不是柴油、电动或汽油的可能性。类extend Vehicle 而不是implementing Vehicle。我之前写过它实现了车辆。道歉。 @Lemmings19:你仍然可以使用Vehicle
类型。当子类扩展父类时,子类确实实现了父类方法:)
是的,但是当我尝试调用存在于 Diesel
中的东西时,它找不到它,因为它只在 Vehicle
中查找。不过,如果我只需要调用 Vehicle
中存在的东西,那就可以了。无论如何,感谢您的回复!【参考方案4】:
正如你在问题开始时所说:
我有一个抽象类,我们称之为 Vehicle。柴油、电动和燃气器具类车辆。
因此,Vehicle 是超类,而 Diesel、Electric 和 Gas 是专业化。
db 上的表可以反映这种布局
Vehicle (id, brand, price, type) // type could help you selecting electric,gas,diesel
Electric(vehicle_id, volts, amp, ohms, other_specific_stuff)
Gas(vehicle_id, pressure, gas_type, other_specific_stuff)
Diesel(vehicle_id, other_specific_stuff)
当您查询您的车辆时,您可以使用左外连接
select * from vehicle as v
left outer join electric as e on (v.id = e.vehicle_id)
left outer join gas as g on (v.id = g.vehicle_id)
left outer join diesel as d on (v.id = d.vehicle_id)
然后您使用类型字段映射到特定的子类。
当你存储或更新数据库中的对象时,你需要一个方法来存储公共部分:
void update(Vehicle v)
// store v.* in Vehicle table
以及为特定汽车存储其余部分的方法
void update(Electric e)
update(v)
// store e.* on Electric table
删除和插入也一样。
【讨论】:
感谢表的 SQL 注释,但这并不能避免为每个操作创建 4 个方法,而仅使用示例类。 (Vehicle
、Diesel
、Electric
、Gas
的创建、读取、更新、删除)总共 16 种方法。为每个操作创建Vehicle
方法可以消除很多重复,但我仍然想知道是否有更有效的方法。
另外,我在问题中犯了一个错误(这不会影响您的回答),说类实现了Vehicle
。他们实际上扩展车辆。我的错,对不起。
Hibernate 为特定的类做了大部分的附加映射,但是在一个干净的设计中,你需要一个用于公共数据的公共表,以及一个用于每个专业化的特定表;那么您可能需要公用表中的一个字段来指示将类型休眠为“鉴别器”;该方法类似于我在回答中展示的方法。请参阅:docs.jboss.org/hibernate/orm/3.3/reference/en/html/… 此链接分析了解决您的问题的几种方法;你可以读一读。
其实,如果你看到了,我发布的更新方法并没有重复。 Electric.update() 使用 Vehicle.update() 避免重复。我不是 hibernate 的超级粉丝,但如果您想避免编写 OR 映射代码,它会在您的示例中为您完成所有工作。【参考方案5】:
我建议将持久层(关系数据库)与面向对象的模型层 (Java) 分离,忘记“CRUD”操作,更多地考虑对象行为。
如果有一个类Garage
实现collection 的Vehicles
,那就太好了:
interface Garage extends Collection<? extends Vehicle>
Vehicle find(int id);
然后,你可以像这样使用它:
Garage garage = new GarageInSql(/* your persistence layer details */);
Electric vehicle = Electric.class.cast(garage.find(123));
int mileage = vehicle.mileage();
vehicle.rename("Chevrolet");
garage.remove(vehicle);
如果你需要控制事务,请引入另外一种方法Garage#commit()
。
【讨论】:
以上是关于高效地执行具有多个相似类的 CRUD 操作?的主要内容,如果未能解决你的问题,请参考以下文章