如何为具有可扩展属性的资源编写 DAO?
我正在尝试用 Java 编写一个嵌入式(不是 Web,不是企业)内容管理系统,重点是组织和易用性以及可扩展至 100,000 个左右的项目。 用户& 系统应该能够创建和定义可以与独特资源相关联的元数据项,以允许搜索。
例如,他们可以创建一个采用字符串值的标签“ProjectName”。 然后他们可以将一堆资源标记为属于“接管世界”或“修理我的汽车”项目。 标签是强类型的,因此标签可以存储单个或多个字符串、整数、双精度数等。每种标签类型都应该具有格式化程序和输入验证程序以允许编辑。
我认为从 GUI 中抽象出存储模型非常重要,以实现可扩展性; 执行此操作的明显方法是对每个资源使用数据访问对象 (DAO)。 但是,我不知道如何编写支持可变数量标签并且能够正确扩展的 DAO。
问题是资源需要同时表现为元组(用于表格查看/排序/过滤)和(TagName,TagValue)映射。 对于每个 GUI 更新,GUI 模型可能会调用这些方法数千次,因此索引的一些概念将使一切工作得更好。 不幸的是,多种标签类型意味着除非我将所有内容作为通用对象返回并执行一堆“TagValue instanceof Type”条件,否则会很尴尬。
我已经研究过使用反射和 Apache 的 DynaBeans,但是对其进行编码以与 GUI 模型一起使用看起来既痛苦又尴尬。 有一个更好的方法吗??? 一些库或设计模式?
所以,我的问题是,有更好的方法吗? 一些库或设计模式可以简化整个事情?
I'm trying to write an embedded (NOT web, not enterprise) content management system in Java, with a focus on organization and ease of use and scalability to 100,000 or so items. The user & system should be able to create and define metadata items which can be associated with unique resources, to allow for searching.
For example, they can create a tag "ProjectName" which takes String values. Then they can tag a bunch of resources as belonging to projects "Take Over the World" or "Fix My Car." The tags are strongly typed, so a tag may store single or multiple string(s), integer(s), double(s), etc. Each tag type should have formatters and input validators to allow editing.
I've decided that it is important to abstract the storage model from the GUI, to allow for scalability; the obvious way to do this is to use data access objects (DAOs) for each resource. However, I can't figure out how to write DAOs that support a variable number of tags and will scale properly.
The problem is that resources need to behave both as tuples (for tabular viewing/sorting/filtering) and as (TagName,TagValue) maps. The GUI models may call these methods potentially thousands of times for each GUI update, so some notion of indexing would make it all work better. Unfortunately, the multiple tag types mean it'll be awkward unless I return everything as a generic Object and do a whole mess of "TagValue instanceof Type" conditionals.
I've looked into using reflection and Apache's DynaBeans, but coding this to work with GUI models looks just painful and awkward. Is there a better way to do this??? Some library or design pattern?
So, my question is, is there a better way? Some library or design pattern that would simply this whole thing?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我认为您不应该将这些属性中的任何一个视为实际的成员变量。 您应该有一个包含属性的“Property”对象(类似于成员变量)和一个包含属性集合的“Collection”对象(类似于类)。
由于这些属性和集合实际上没有与之关联的代码,因此将它们实现为对象是没有意义的(并且会很痛苦)。
您的属性和集合需要保存特定于它们的所有数据。 例如,如果一个字段最终写入数据库,则需要将其表名存储在某处。 如果需要将其写入屏幕,也需要将其存储在某个地方。
范围/值检查可以“添加”到属性中,因此当您定义属性的数据类型时,您可能会看到一些显示“MaxLength(12)”的文本,该文本将实例化一个名为 MaxLength 的类,其值为 12,并将该类存储到属性中。 每当属性的值发生更改时,新值都会传递到已应用于此类的每个范围检查器。 可以有多种类型的与该类关联的操作。
这只是基础。 我已经设计了类似的东西,这是一项大量的工作,但它比尝试用直接的语言来简单得多。
我知道现在这看起来工作量太大了(如果你真的明白我的建议的话应该是这样),但请记住这一点,最终你可能会说“哼,也许这毕竟值得一试” 。
编辑(回复评论):
我考虑过尝试使用注册表/密钥(我们仍在谈论属性值对),但它不太适合。
您正在尝试将 DAO 放入 Java 对象中。 这确实很自然,但我逐渐将其视为解决 DAO/DTO 问题的一种糟糕方法。 Java 对象具有属性和作用于这些属性的行为。 对于您正在做的事情,没有任何行为(例如,如果用户创建“生日”字段,您将不会使用对象代码来计算他的年龄,因为您并不真正知道生日是什么)。
因此,如果您放弃对象和属性,您将如何存储这些数据?
让我开始一个非常简单的第一步(这与您提到的注册表/标签系统非常接近):在您将使用对象的地方,使用哈希表。 对于属性名称,请使用键;对于属性值,请使用哈希表中的值。
现在,我将介绍为增强这个简单模型而遇到的问题和解决方案。
问题:
你已经失去了强类型,并且你的数据格式非常自由(这可能很糟糕)
解决方案:
为“Attribute”创建一个基类,用于代替哈希表中的值。 为 IntegerAttribute、StringAttribute、DateAttribute 扩展该基类...不允许不适合该类型的值。 现在你有了强类型,但它是运行时而不是编译时——这可能没问题,因为你的数据实际上是在运行时定义的。
问题:
格式化器和验证器
解决方案:
能够为您的属性基类创建插件。 您应该能够对任何属性“setValidator”或“setFormatter”。 验证器/格式化程序应该与属性一起存在——因此当您保存属性时,您可能必须能够将它们序列化到数据库。
这里的好处是,当您对属性执行“attribute.getFormattedValue()”时,它会预先格式化以供显示。 attribute.setValue() 将自动调用验证器,并在任何验证失败时抛出异常或返回错误代码。
问题:
如何在屏幕上显示这些内容? 我们已经有 getFormatted() 但它显示在屏幕上的哪里? 我们用什么作为标签? 什么样的控件应该编辑此字段?
解决方案:
我会将所有这些内容存储在每个属性中。 (订单应该存储在类中,但由于这是一个哈希表,所以它不起作用——我们接下来会讨论这个问题)。 如果您存储显示名称、用于呈现此内容的控件类型(文本字段、表格、日期...)以及数据库字段名称,则此属性应该具有与显示和数据库交互所需的所有信息 I/ O 为处理属性而编写的例程。
问题:
对于 DAO 来说,哈希表是一个很糟糕的接口。
解决方案:
这是绝对正确的。 您的哈希表应该包装在一个知道它所拥有的属性集合的类中。 它应该能够将自身(包括其所有属性)存储到数据库中——可能需要辅助类的帮助。 它应该能够通过单个方法调用来验证所有属性。
问题:
如何实际使用这些东西?
解决方案:
由于它们包含自己的数据,因此在系统中它们交互的任何点(例如与屏幕或数据库),您都需要一个“适配器”。
假设您正在呈现一个屏幕来编辑数据。 您的适配器将传递一个框架和一个基于哈希表的 DTO。
首先,它将按顺序遍历属性列表。 它会询问第一个属性(例如字符串)要使用哪种控件进行编辑(例如文本字段)。
它将创建一个文本字段,然后向该文本字段添加一个侦听器来更新数据,这会将您的数据绑定到屏幕上的控件。
现在,每当用户更新控件时,更新都会发送到属性。 该属性存储新值,您就完成了。
(一次传输所有值的“确定”按钮的概念会让这变得复杂,但我仍然会事先设置每个绑定并使用“确定”作为触发器。)
这种绑定可能很困难。 我已经手动完成了,一旦我使用了一个名为“JGoodies”的工具包,它内置了一些绑定功能,这样我就不必自己编写每个可能的绑定组合,但从长远来看我不确定它节省了很多时间。
这太长了。 有一天我应该创建一个 DAO/DTO 工具包——我认为 Java 对象根本不适合作为 DAO/DTO 对象。
如果您仍然感到困惑,请随时给我发电子邮件/即时消息 - bill.kress at gmail..
I don't think you should consider any of these properties as actual member variables. You should have a "Property" object that contains a property (which would be analogous to a member variable), and a "Collection" object that has collections of properties (which would be like a class).
Since these attributes and collections don't really have code associated with them, it would make no sense to implement them as objects (and would be a real pain in the butt)
Your attributes and collections need to hold ALL the data specific to them. For instance, if a field is eventually written to the database, it needs to have it's table name stored somewhere. If it needs to be written to the screen, that also needs to be stored somewhere.
Range/value checking can be "Added" to the attributes, so when you define what type of data an attribute is, you might have some text that says "MaxLength(12)" which would instantiate a class called MaxLength with the value 12, and store that class into the attribute. Whenever the attribute's value changes, the new value would be passed to each range checker that has been applied to this class. There can be many types of actions associated with the class.
This is just the base. I've designed something like this out and it's a good deal of work, but it's much simpler than trying to do it in a straight language.
I know that this seems like WAY too much work right now (it should if you actually get what I'm suggesting), but keep it in mind and eventually you'll probably go "Hmph, maybe that was worth a try after all".
edit (response to comment):
I thought about trying to work with the registry/key thing (we're still talking attribute value pairs), but it doesn't quite fit.
You are trying to fit DAOs into Java Objects. This is really natural, but I've come to see it as just a bad approach to solving the DAO/DTO problem. A Java Object has attributes and behaviors that act on those attributes. For the stuff you are doing, there are no behaviors (for instance, if a user creates an "Birthday" field, you won't be using object code to calculate his age because you don't really know what a birthday is).
So if you throw away having Objects and attributes, how would you store this data?
Let me go with a very simple first step (that is very close to the registry/tag system you mentioned):Where you would have used an object, use a hashtable. For your attribute names use keys, for the attribute values, use the value in the hashtable.
Now, I'll go through the problems and solutions I took to enhance this simple model.
Problem:
you've lost Strong Typing, and your data is very free-format (which is probably bad)
Solution:
Make a base class for "Attribute" to be used in the place of the value in the hashtable. Extend that base class for IntegerAttribute, StringAttribute, DateAttribute, ... Don't allow values that don't fit that type. Now you have strong typing, but it's runtime instead of compile time--probably okay since your data is actually DEFINED at runtime anyway.
Problem:
Formatters and Validators
Solution:
Have the ability to create a plug-in for your attribute base-class. You should be able to "setValidator" or "setFormatter" for any attribute. The validator/formatter should live with the attribute--so you probably have to be able to serialize them to the DB when you save the attribute.
The nice part here is that when you do "attribute.getFormattedValue()" on the attribute, it's pre-formatted for display. attribute.setValue() will automatically call the validator and throw an exception or return an error code if any of the validations fail.
Problem:
How do I display these on the screen? we already have getFormatted() but where does it display on the screen? what do we use for a label? What kind of a control should edit this field?
Solution:
I'd store all these things inside EACH attribute. (The order should be stored in the Class, but since that's a hashtable so it won't work--well we'll get to that next). If you store the display name, the type of control used to render this (text field, table, date,...) and the database field name, this attribute should have all the information it needs to interact with display and database I/O routines written to deal with attributes.
Problem:
The Hashtable is a poor interface for a DAO.
Solution:
This is absolutely right. Your hashtable should be wrapped in a class that knows about the collection of attributes it holds. It should be able to store itself (including all its attributes) to the database--probably with the aid of a helper class. It should probably be able to validate all the attributes with a single method call.
Problem:
How to actually work with these things?
Solution:
Since they contain their own data, at any point in your system where they interact (say with the screen or with the DB), you need an "Adapter".
Let's say you are presenting a screen to edit your data. Your Adapter would be passed a frame and one of your hashtable-based DTOs.
First it would walk through the list of attributes in order. It would ask the first attribute (say a string) what kind of control it wanted to use for editing (let's say a text field).
It would create a text field, then it would add a listener to the text field that would update the data, this binds your data to the control on the screen.
Now whenever the user updates the control, the update is sent to the Attribute. The attribute stores the new value, you're done.
(This will be complicated by the concept of an "OK" button that transfers all the values at once, but I would still set up each binding before hand and use the "OK" as a trigger.)
This binding can be difficult. I've done it by hand, once I used a toolkit called "JGoodies" that had some binding ability built in so that I didn't have to write each possible binding combination myself, but I'm not sure in the long-run it saved much time.
This is way too long. I should just create a DAO/DTO toolkit someday--I think Java Objects are not at all suited as DAO/DTO objects.
If you're still stumped, feel free to Email/IM me-- bill.kress at gmail..
我从你的问题中假设“资源”是系统中的一个实体,它有一些与之关联的“标签”实体。 如果我的假设是正确的,这里有一个普通的 DAO 接口,请告诉我这是否是您的想法:
这里的想法是,您将为可用的任何数据存储机制实现此接口,并且应用程序将通过以下方式访问它:这个界面。 实现类的实例将从工厂获得。
这符合你的想法吗?
您提到的问题的另一个方面是必须应对多个不同的 TagType,这些 TagType 需要不同的行为,具体取决于类型(需要“TagValue instanceof Type”条件)。 访客模式可以以一种优雅的方式为您处理这个问题。
I assume from your question that a "resource" is an entity in your system that has some "tag" entities associated with it. If my assumption is correct, here's a vanilla DAO interface, let me know if this is what you're thinking:
The idea here is that you would implement this interface for whatever data storage mechanism you have available, and the application would access it via this interface. Instances of implementation classes would be obtained from a factory.
Does this fit with what you're thinking?
The other aspect of the problem you mention is having to contend with multiple different TagTypes that require different behavior depending on the type (requiring "TagValue instanceof Type" conditionals). The Visitor pattern may handle this for you in an elegant way.
您是否习惯使用关系数据库? 研究面向文档的数据库(例如 couchDB)可能是值得的。 它将为您提供存储所需的任意强类型对象所需的灵活性,并让您能够查询这些对象。 我相信也有一些用于访问 couchDB 的 Java 库。
Are you tied to using a relational database? It might be worthwhile to look into a document oriented database such as couchDB. It will give you the flexibility that you need to store any arbitrary strongly typed object that you want and give you the ability to query those objects as well. I believe there are some Java libraries for accessing couchDB also.