在Hibernate代理存在的情况下,我究竟需要在哪里避免直接字段访问?

时间:2014-05-20 03:22:44

标签: java hibernate jpa proxy

我有一个简单的实体类如下:

@Entity public class Foo {

    @NotNull @Column private String name;

    public String getName() { return this.name; }

    public void setName(String name) { this.name = name; }
}

现在面对Hibernate代理,我知道我不能依赖getClass()返回Foo.class,并且面对实体层次结构我不能依赖instanceof。我知道我不应该直接使用other.name方法访问equals。例如,我不应该这样做:

// in class Foo:
@Override public boolean equals(Object obj) {

    // instanceof is fine in this case since Foo has no entity superclass
    if (!(obj instanceof Foo)) {
        return false;
    }
    Foo other = (Foo) obj;

    // hold up! if other is a proxy, then other.name will be null
    return Objects.equal(name, other.name);
}

我的问题是,完全在哪里访问other.name是不安全的?访问this.name是不安全的?

这很快就会投入生产,我在这里没有“试一试”的奢侈品。

1 个答案:

答案 0 :(得分:0)

为了回答这个问题,我想起了我的Foo课程来报告不同背景下的现场访问。这是我添加的内容:

public String callGetNamePublic() {
    System.out.print("callGetNamePublic: ");
    return getNamePublic();
}

public String getNamePublic() {
    System.out.println("getNamePublic: " + name);
    return name;
}

public String callGetNameProtected() {
    System.out.print("callGetNameProtected: ");
    return getNameProtected();
}

protected String getNameProtected() {
    System.out.println("getNameProtected: " + name);
    return name;
}

public String callGetNamePackage() {
    System.out.print("callGetNamePackage: ");
    return getNamePackage();
}

String getNamePackage() {
    System.out.println("getNamePackage: " + name);
    return name;
}

public String callGetNamePrivate() {
    System.out.print("callGetNamePrivate: ");
    return getNamePrivate();
}

private String getNamePrivate() {
    System.out.println("getNamePrivate: " + name);
    return name;
}

public static class FooSubclass extends Foo {

    public String callOtherGetNamePublic(Foo other) {
        System.out.print("callOtherGetNamePublic: ");
        return other.getNamePublic();
    }

    public String callOtherGetNameProtected(Foo other) {
        System.out.print("callOtherGetNameProtected: ");
        return other.getNameProtected();
    }

    public String callOtherGetNamePackage(Foo other) {
        System.out.print("callOtherGetNamePackage: ");
        return other.getNamePackage();
    }

    public String callOtherGetNamePrivate(Foo other) {
        System.out.print("callOtherGetNamePrivate: ");
        return other.getNamePrivate();
    }

    public String accessOtherName(Foo other) {
        System.out.print("accessOtherName: " + other.name);
        return other.name;
    }
}

然后我构建了一个测试来提取Foo代理对象,然后调用这些方法。像这样:

// I'll leave this to your imagination ;)
Foo foo1 = getFooProxy();

foo1.getName();
foo1.getNamePublic();
foo1.callGetNamePublic();
foo1.callGetNameProtected();
foo1.callGetNamePackage();
foo1.callGetNamePrivate();

FooSubclass foo2 = new Foo.FooSubclass();
foo2.callOtherGetNamePublic(foo1);
foo2.callOtherGetNameProtected(foo1);
foo2.callOtherGetNamePackage(foo1);
foo2.callOtherGetNamePrivate(foo1);
foo2.accessOtherName(foo1);

结果如下:

getNamePublic: testName
callGetNamePublic: getNamePublic: testName
callGetNameProtected: getNameProtected: testName
callGetNamePackage: getNamePackage: testName
callGetNamePrivate: getNamePrivate: testName
callOtherGetNamePublic: getNamePublic: testName
callOtherGetNameProtected: getNameProtected: testName
callOtherGetNamePackage: getNamePackage: testName
callOtherGetNamePrivate: getNamePrivate: null
accessOtherName: null

我发现您需要避免在任何最终方法中直接访问字段。如果您将以下任何方法更改为最终,相应的结果将从testName更改为null

  • getNamePublic
  • getNameProtected
  • getNamePackage
  • getNamePrivate

所以答案是:面对Hibernate代理的直接字段访问的唯一限制是(1)对other.name的直接字段访问,(2)other内私有方法的调用, (3)在任何最终方法中。