在服务类中,我有一个从方法调用的@Transactional
方法。我已经验证在调用此代码时我有一个事务处于活动状态。我意识到我应该没有 DA 层,但我正在使用一个遗留应用程序,这使得做事“正确”的方式比此时的价值更麻烦。
映射如下所示:
public class Foo {
private String id;
private Bar bar;
@Id
@Column(name = "FOO_ID", unique = true, nullable = false, length = 16)
@GeneratedValue(generator = "blahIdSeq")
@GenericGenerator(name = "blahIdSeq",
strategy = "org.blah.CustomIdGenerator")
public String getId() {return id;}
@JoinColumn(name = "FOO_ID")
@OneToOne(fetch = FetchType.LAZY, optional = false)
public Bar getBar() { return bar; }
// SETTERS INCLUDED
}
public class Bar {
private String id;
private Foo foo;
@Id
@Column(name = "FOO_ID")
@GeneratedValue(generator = "someSeq")
@GenericGenerator(name = "someSeq",
strategy = "foreign",
parameters = {
@Parameter(name = "property", value = "foo")
})
public String getId() { return id; }
@OneToOne(fetch = FetchType.LAZY)
@PrimaryKeyJoinColumn(name = "FOO_ID")
public Foo getFoo() { return foo; }
// SETTERS INCLUDED
}
该方法看起来像这样:
public String createFoo(Foo foo) {
Session ses = getSessionFactory().getCurrentSession();
Bar bar = new Bar();
bar.setFoo(foo);
foo.setBar(bar);
ses.save(foo);
ses.save(bar);
System.out.println(foo.getId()); // yields the ID value set by generator
System.out.println(bar.getId()); // yields same ID value as above
ses.flush();
ses.refresh(foo);
}
现在,将org.hibernate.SQL
日志记录设置为DEBUG
,我可以看到 Foo 和 Bar 的插入语句都已创建,但调用刷新后的刷新会引发org.hibernate.UnresolvableObjectException: No row with the given identifier exists
异常。
什么可能导致这种情况?使用的数据库是 Oracle 11gR2。
更新
我已将问题缩小到会话。似乎调用currentSession.flush()
并没有按预期将数据写入数据库以进行刷新。如果我注释掉该方法的其余部分,它将在最后提交,并且所有内容都将在数据库中。
但是,执行刷新/刷新不会返回水合对象,因此我不能在以后的事务中使用数据库填充的值(由列默认设置)。我也不能将事务拆分为多个,因为我需要能够在方法中的任何点回滚。
关于为什么刷新没有为我提供数据库中可访问的数据的任何想法?
另一个更新
我已经移动了很多代码只是为了尝试隔离问题,但我仍然遇到问题。我还摆脱了两个实体之间的关系,尝试手动处理所有事情,看看这是否能解决问题。考虑到史蒂夫的所有评论,这就是我现在所拥有的:
public class Foo {
private String id;
private Bar bar;
@Id
@Column(name = "FOO_ID", unique = true, nullable = false, length = 16)
@GeneratedValue(generator = "blahIdSeq")
@GenericGenerator(name = "blahIdSeq",
strategy = "org.blah.CustomIdGenerator")
public String getId() {return id;}
// SETTERS INCLUDED
}
public class Bar {
private String id;
private Foo foo;
@Id
@Column(name = "FOO_ID")
public String getId() { return id; }
// SETTERS INCLUDED
}
@Service('fooService')
@Transactional(readOnly = true)
class FooService {
@Autowired
SessionFactory sessionFactory // populated using Spring config:
// org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean
@Transactional(readOnly = false)
public void doSomeStuff(Foo fooToSave) {
sessionFactory.getCurrentSession().saveOrUpdate(fooToSave);
Bar bar = new Bar(fooToSave); // this populates the Bar.Id field
sessionFactory.getCurrentSession().saveOrUpdate(bar);
sessionFactory.getCurrentSession().flush();
sessionFactory.getCurrentSession().refresh(fooToSave); // exception thrown here
}
}
另一个更新
在 Oracle 领域进行了相当多的尝试以确保 SQL 在同一个会话等上运行之后,我发现了这个问题。即使 Hibernate 记录了正在设置 SQL 绑定变量,但实际上并没有。使用 Oracle 11gR2 V$SQL_BIND_CAPTURE
,我可以看到使用最后执行的 SQL ID(验证为插入语句)有 24 个绑定变量,并且没有一个绑定了值。仍然不确定是什么导致这些值变为空白,但我离找到答案更近了一点。这一定是我的映射有问题,我不能完整地放在这里。
作为绑定变量,我猜 Oracle 不会因为无法插入而感到不适。JDBC 通常只返回为验证语句插入的行数INSERT
,但我不确定 Hibernate 抽象是如何处理这些东西的。我目前正在使用 Hibernate 3.6.10——从 3.6.5 升级,看看它是否可以解决问题。它没有。:P
我被误导了
忽略上面的“还有一个更新”部分。V$SQL_BIND_CAPTURE
绑定变量似乎在事务提交之前不会出现在视图中。回到绘图板。
另一个修订 - 我发誓我会被禁止
我决定回到基础。自从它处于工作状态以来,我发生了什么变化?主要是映射。一些服务层项目也发生了变化,但主要是将我们的 Hibernate 映射从 XML 移动到注释。因此,我采用了我一直在使用的相同服务方法,注释掉了所有其他内容,并尝试做与我尝试Foo
使用另一种持久对象类型所做的完全相同的事情。你猜怎么着?这样可行。在这一点上,唯一可能导致我心痛的链接是我拥有的映射Foo
。我怀疑我的雇主是否希望我在 SO 上提供全部资源,所以我可能不得不自己解决这个问题。当我最终弄清楚时,我会以某种身份发布答案。
成功!但我不确定为什么...
这是给我带来麻烦的代码。请记住,这BAZ
是一个链接表,其复合 ID 由一个@Embeddable
(在本示例中称为“键”)组成,包括FOO_ID
引用表中的一行FOO
和STATE_ID
引用另一个表。
public class Foo {
// OTHER FIELDS INCLUDING IDs AND SUCH
private Baz bazOfDoom;
private Baz bazOfLight;
private Set<Baz> allTheBaz;
@OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.REFRESH)
@JoinColumns({
@JoinColumn(name = "FOO_ID", referencedColumnName = "FOO_ID", insertable = false, updatable = false, nullable = false)
@JoinColumn(name = "DOOM_ID", referencedColumnName = "STATE_ID", insertable = false, updatable = false, nullable = false)
})
public Baz getBazOfDoom() { return bazOfDoom; }
@OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.REFRESH)
@JoinColumns({
@JoinColumn(name = "FOO_ID", referencedColumnName = "FOO_ID", insertable = false, updatable = false, nullable = false)
@JoinColumn(name = "LIGHT_ID", referencedColumnName = "STATE_ID", insertable = false, updatable = false, nullable = false)
})
public Baz getBazOfLight() { return bazOfLight; }
@OneToMany(mappedBy = "key.foo", fetch = FetchType.LAZY, cascade = CascadeType.REFRESH)
public Set<Baz> getAllTheBaz() { return allTheBaz; }
}
我移除了级联并且它起作用了。我不知道为什么。谁能解释这将得到我的“正确答案”标记。:)