我目前正在使用良好的面向对象原则,在hibernate中,我有一个POJO,其中的属性将被动态填充。这是我为良好的面向对象设计所读到的一种设计模式,在这种模式中,可以很容易地向特定对象添加属性,而不会破坏应用程序我的问题是,当您的属性被认为是动态的时,如何将其映射到表,我使用枚举来限制映射的键值对,但理想情况下它仍然可以增长。我只使用内存中数据库(h2),不会将代码用于生产用途。这仅用于学习目的。参见下面的代码:
public class Transaction {
private static Map<Object, Object> properties;
public Transaction(){
if(null != properties)
properties = new LinkedHashMap<Object, Object>();
}
public Transaction(Map<Object, Object> properties){
if(null != properties)
setProperties(properties);
}
public void setProperties(Map<Object, Object> prop){
properties = prop;
}
public void setProperties(Properties property, String value){
properties.put(property, value);
}
public Map<Object, Object> getProperties(){
return properties;
}
public String getProperties(Properties property){
return (String) properties.get(property);
}
}
因此,我希望能够创建一个具有这些属性的表,我的枚举:
public enum Properties {
Entry("Entry"), Id("Entry_ID"), Name("Name"), Credit("Credit");
private final String description;
private Properties(final String description){
this.description = description;
}
@Override
public String toString(){
return description;
}
}
我有这个hibernate映射,但正如你所看到的,每次更新字段时都需要更新,我需要一个通用映射,这样当我更改/添加属性、注释或xml时就可以了,请参阅下面:
<class name="Transaction" table="TRANSACTION">
<id name="id" column="ENTRY_ID">
<generator class="increment"/>
</id>
<property name="name"/>
<property name="credit" type="boolean" column="IS_CREDIT"/>
</class>
至于Hibernate:它实际上是为将表静态绑定到对象而设计的,如果在运行时更改模式,可能会遇到重大问题。不过,您可以实现以下解决方案:
- 序列化的LOB(将Map序列化为二进制字段,或者使用JSON/XML将其序列化为文本字段)。这是一种半吊子的方法——一半是表格/普通形式/SQL,一半不是SQL。因此,如果这种方法很有吸引力,您可能需要考虑使用稍后讨论的NoSQL数据库
属性表,其中自定义属性存储在连接到主表的键值对表中。这可以在Hibernate中使用Indexed Collections进行映射(请参阅第7.2.2.2节映射),您最终会得到类似于您的问题的东西:
@Entity public class Transaction { @Id @GeneratedValue public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } private Integer id; // ... snip ... @OneToMany(mappedBy="transaction") @MapKey(name="name") public Map<String, String> getProperties(){ return properties; } public void setProperties(Map<String, String> prop){ properties = prop; } private Map<String, String> properties; // NB: Type has to be <String, String> because the column name is a String and you have defined the property value to be a String. public void setProperty(Properties property, String value){ properties.put(property, value); } public String getProperty(String name){ return (String) properties.get(property); } } @Entity public class Property { @Id @GeneratedValue public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } private Integer id; @ManyToOne public Transaction getTransaction() { return transaction; } public void setTransaction(Transaction transaction) { this.transaction = transaction; } private Transaction transaction; public String getName() { return name; } public void setName(String name) { this.name = name; } private String name; public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } private String description; }
预定义的自定义字段,从一个包含大量未使用列的非常宽的表开始。在这个实现中,您最终定义了任意属性名和预定义列名(
getString1()
、getString10()
等)之间的映射
然而,对您来说,更好的解决方案可能是使用NoSQL数据库,特别是基于文档的数据库。这些允许您存储和检索任意数据结构(映射和列表)。有趣的是,使用这样的方法使绑定到数据存储变得更加容易。
MongoDB或Redis(Jedis的Java绑定)就是一个例子。