And*_*ndy 5 java spring hibernate
在服务类中,我有一个从@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
}
Run Code Online (Sandbox Code Playgroud)
该方法看起来像这样:
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);
}
Run Code Online (Sandbox Code Playgroud)
现在,在org.hibernate.SQL设置日志记录的情况下DEBUG,我可以看到创建了Foo和Bar的insert语句,但调用flush之后的刷新会引发org.hibernate.UnresolvableObjectException: No row with the given identifier exists异常.
什么可能导致这个?使用的数据库是Oracle 11gR2.
我已将问题缩小到会议.似乎调用currentSession.flush()不是按照预期的那样将数据写入数据库进行刷新.如果我注释掉方法的其余部分,它将在最后提交,一切都将在数据库中.
但是,执行刷新/刷新不会返回水合对象,因此我不能在事务中使用数据库填充的值(由列默认值设置).我也无法将事务拆分为多个事务,因为我需要能够在方法中的任何位置回滚.
关于为什么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
}
Run Code Online (Sandbox Code Playgroud)
public class Bar {
private String id;
private Foo foo;
@Id
@Column(name = "FOO_ID")
public String getId() { return id; }
// SETTERS INCLUDED
}
Run Code Online (Sandbox Code Playgroud)
@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
}
}
Run Code Online (Sandbox Code Playgroud)
在Oracle-land中进行了相当多的游戏以确保SQL在同一个会话中运行之后,我发现了这个问题.即使Hibernate正在记录正在设置SQL绑定变量,但它们实际上并非如此.使用Oracle 11gR2 V$SQL_BIND_CAPTURE,我能够看到使用最后执行的SQL ID(验证为insert语句)有24个绑定变量,而其中没有一个绑定了它的值.仍然不确定是什么导致价值空白,但我更接近找到我的答案.它必须是我的映射的问题,我不能完全放在这里.
作为绑定变量,我猜测Oracle不会因为无法插入而变得合适.JDBC通常只返回INSERT为验证语句插入的行数,但我不确定Hibernate抽象是如何处理这些东西的.我目前正在使用Hibernate 3.6.10 - 从3.6.5升级,看它是否可以解决问题.它没有.:P
忽略上面的"YET ANOTHER UPDATE"部分.绑定变量似乎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; }
}
Run Code Online (Sandbox Code Playgroud)
我删除了级联,它工作.我不知道为什么.无论谁能解释,都会得到我的"正确答案"标记.:)
看来您的对象在将其保存到数据库后不拥有对象的标识符,从而导致调用刷新()时出现异常。
事实上,假设您的数据库表自己的主键定义为auto-increment。因此,当您保存第一个 Foo 对象时,主键列的值为:1。
然而,Hibernate 必须在调用 save() 方法后知道这个新生成的标识符!
做到这一点的最佳方法是期望 Hibernate 在对象保存到数据库后立即重新影响良好的标识符。
因此,您可能会在实体类中错过这一行,以便在将对象保存在数据库中时自动提供标识符:
@GeneratedValue(strategy = GenerationType.AUTO)
Run Code Online (Sandbox Code Playgroud)
当然,您不必自动生成它们,而是可以手动精确生成它们。