使用Datanucleus

时间:2018-04-09 20:17:49

标签: java mongodb datanucleus

我和我的团队正在努力升级我们公司的系统,因为它被遗忘了,并且正在运行它使用的所有东西的旧版本;因此,开发更新的功能正在成为新技术和不支持技术的痛苦。

到目前为止,我们已经设法生成了一个几乎完全可用的系统版本;但是我们陷入了一个涉及Datanucleus-JDO,MongoDB和继承的功能。

我们有一些非常类似的模型(来自代码的预期)。在当前的生产中版本中,对它应用更改通常涉及在所有类中重写相同的代码段,因此我们认为继承将使工作更容易和更好。所以我们在顶级层次上有两个接口(据我们所知,Datanuclues和MongoDB根本不关心它们);这是这样的:

public interface Entity extends Serializable {

    String getDate();
    double getQty();
    void setQty(double qty);
    void setDate(String date);
    void setKey(Key key);

}

public interface HourEntity extends Entity {

    String getHour();

}

我们使用应用程序定义的键,我们使用这个唯一的类来构建不同类型的键。我们只希望此类的toString表示在Mongo中进行sotre和检索数据。

public final class Key implements Serializable {
    static final long serialVersionUID = -448150158203091507L;
    public final String targetClassName;
    public final String id;
    public final String toString;
    public final int hashCode;

    public Key() {
        targetClassName = null;
        id = null;
        toString = null;
        hashCode = -1;
    }

    public Key(String str) {
        String[] parts = str.split("\\(");
        parts[1] = parts[1].replaceAll("\\)", " ");
        parts[1] = parts[1].replace("\"", " ");
        parts[1] = parts[1].trim();
        this.targetClassName = parts[0];
        this.id = parts[1];
        toString = this.toString();
        hashCode = this.hashCode();
    }

    public Key(String classCollectionName, String id) {
        if (StringUtils.isEmpty(classCollectionName)) {
            throw new IllegalArgumentException("No collection/class name specified.");
        }
        if (id == null) {
            throw new IllegalArgumentException("ID cannot be null");
        }
        targetClassName = classCollectionName;
        this.id = id;
        toString = this.toString();
        hashCode = this.hashCode();
    }

    public String getTargetClassName() {
        return targetClassName;
    }

    public int hashCode() {
        if(hashCode != -1) return hashCode; 
        int prime = 31;
        int result = 1;
        result = prime * result + (id != null ? id.hashCode() : 0);
        result = prime * result + (targetClassName != null ? targetClassName.hashCode() : 0);
        return result;
    }

    public boolean equals(Object object) {
    if (object instanceof Key) {
        Key key = (Key) object;
        if (this == key)
            return true;
        return targetClassName.equals(key.targetClassName) && Objects.equals(id, key.id);
    } else {
        return false;
    }
}

    public String toString() {
        if(toString != null) return toString;
        StringBuilder buffer = new StringBuilder();
        buffer.append(targetClassName);
         buffer.append("(");
        if (id != null) {
            buffer.append((new StringBuilder()).append("\"").append(id)
                    .append("\"").toString());
        } else {
            buffer.append("no-id-yet");
        }
        buffer.append(")");
        return buffer.toString();
    }

}

这个apllication定义的标识在所有其他不涉及iheritance的模型上都能正常工作。

这是我们打算存储在数据存储区中的实际模型之一:

@PersistenceCapable(detachable="true")
@Inheritance(strategy=InheritanceStrategy.COMPLETE_TABLE)
public class Ticket implements Entity {

    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.UNSPECIFIED, column="_id")
    protected Key key;

    protected String date;
    protected int qty;

    public Ticket() {
        this.qty = 0;
    }

    public Key getKey() {
        return key;
    }

    @Override
    public void setKey(Key key) {
        this.key = key;
    }

    public double getQty() {
        return qty;
    }

    public void setQty(double qty) {
        this.qty = (int) qty;
    }

    public String getDate() {
        return date;
    }

    public void setDate(String date) {
        this.date = date;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((key == null) ? 0 : key.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Ticket other = (Ticket) obj;
        if (key == null) {
            if (other.key != null)
                return false;
        } else if (!key.equals(other.key))
            return false;
        return true;
    }

    @Override
    public String toString() {
        return "Ticket [key=" + key + ", date=" + date + ", qty="
                + qty + "]";
    }

}

这是它的子类(涉及这个问题的所有模型只涉及一个超类,每个超类只有一个子):

@PersistenceCapable(detachable="true")
@Inheritance(strategy=InheritanceStrategy.COMPLETE_TABLE)
public class HourTicket extends Ticket implements HourEntity {

    private String hour;

    public HourTicket() {
        super();
    }

    public Key getKey() {
        return key;
    }

    @Override
    public void setKey(Key key) {
        this.key = key;
    }

    public String getHour() {
        return hour;
    }

    public void setHour(String hour) {
        this.hour = hour;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((key == null) ? 0 : key.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        HourTicket other = (HourTicket) obj;
        if (key == null) {
            if (other.key != null)
                return false;
        } else if (!key.equals(other.key))
            return false;
        return true;
    }

    @Override
    public String toString() {
        return "HourTicket [key=" + key + ", date=" + date
                + ", hour=" + hour + ", qty=" + qty + "]";
    }

}

最后,persisntance.xml就像这样

<?xml version="1.0" encoding="UTF-8" ?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
        http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd" version="2.0">

    <!-- JOSAdmin "unit" -->
    <persistence-unit name="ourdatastore">
        <class>mx.ourdomain.Ticket</class>
        <class>mx.ourdomain.HourTicket</class>
        <exclude-unlisted-classes/>

    </persistence-unit>
</persistence>

和package-mongo.orm

<?xml version="1.0"?>
<!DOCTYPE orm SYSTEM "file:/javax/jdo/orm.dtd">
<orm>
    <package name="mx.ourdomain" >
        <class name="Ticket" table="Ticket">
            <field name="key" primary-key="true" >
                <column name="_id" length="100" />
            </field >
        </class>

        <class name="HourTicket" table="HourTicket">
            <primary-key >
                <column name="_id" target="_id" />
            </primary-key>
        </class>
     </package>
</orm>

因此,当尝试使用超类或子类执行任何读取或写入操作时会出现问题。对于几个(我们所知道的所有可能)场景中的相同确切结果,这已经令人满意,但我们研究的测试场景始于此调用:

Ticket ticket = persistenceManager.getObjectById(Ticket.class, key);

使用标准程序生成密钥,其他模型使用该程序成功存储和读取;当然,它是之前显示的关键类。

我们已经远远超出了除此之外的数据核心任务。我们发现如预期的那样:

  1. 元数据显示它是其他人的超类。
  2. 使用应用程序管理密钥。
  3. 但是当试图获取类名来确定哪个是要查询的正确Mongo集合时,datanucleus-mongodb尝试查询这两个类(Ticket和HourTicket);但然后它处理mongo驱动程序对象 perse ,然后抛出 CodecConfigurationException ,因为mongo不知道如何使用key类(构建查询时,datanucleus-mongo创建一个具有结构{_id:key}的BasicDBObject,由于条目而无法在没有编解码器的情况下构建。这发生在MongoDBUtils类中在datanucleus-mongodb项目v5.1.0中;类MongoDBUtils,方法getClassNameForIdentity(Object,AbstractClassMetaData,ExecutionContext,ClassLoaderResolver))。

    所以,我们假设我们有一些配置缺失告诉datanucleus它应该使用密钥的toString()形式;因为Monogo驱动程序处理String就好了(datanuclues docs实际上声明当使用自定义类作为数据存储区时,它将使用密钥的toString()形式;所以我不确定这是否可能是一个错误。)

    我们尝试使用KeyTraslator插件并使密钥类成为DatastoreId并在StringId中包装但没有成功:触发相同的异常,除非在StringId中包装Key类:mongo讲座已成功但是然后当试图构建模型对象时,抛出ClassCastException,因为String不能被转换为Key,并且重构代码以使用String键将严重破坏数据库中已有的数据;因为它有一个特殊的格式,密钥类可以读取和生成。

    使用带有datoucleus JDO w / mongoDB的继承是否有遗漏的东西?

1 个答案:

答案 0 :(得分:0)

我没有太多关注objectIdClass元数据周围的设置;因为从我得到的文档中,它们仅用于组合键。结果是如果你定义一个只有一个属性的objectId类;然后它表现为自定义SingleFieldId;这就是我们想要的。

我发现“有趣”的事实是,未注释(或objectIdClass的非声明元数据)类可以正常工作,并且使用的自定义键将被很好地利用;但是一旦你把它们中的任何一个变成超级类,那么你就有义务添加objectIdClass元数据。

除了使用objectIdClass注释Ticket类(以及所有其他超类)之外,我们:

  • 从Key类中删除了toString和hashCode属性(@NotPersistent和transient关键字不会使Datanucleus忽略它们;所以我想现在自定义键上的toString()和hashCode()方法没有性能改进)
  • 从Key类属性中删除了所有 final 限定符(Datanucleus docs并未说自定义键字段不能是最终的;但猜猜是什么,它们不能是)
  • 更改了 String id 的所有超类中的 Key key 类成员(如在密钥类中)。我们还必须改变id成员的getter和setter的实现;在调用方法时使用键类所需的字符串构造函数来构建键。当然,package-mongo.orm中声明的“key”字段在超类中被更改为 id

就是这样!随着这些微小的变化,我们的系统运作良好;其他持久性类别和DAO都不需要进行其他更改。