我有一个母公司 Stock
它有一个子实体 StockDetails
一对一的关系。我不知道如何正确设置和替换 Stock.details
现场。
以下是我的实体类(@getter/@setter来自lombok):
@Getter
@Setter
@NoArgsConstructor
@Entity
@Table(name = "stocks")
public class Stock
{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String isin;
private String symbol;
private String name;
@OneToMany(mappedBy = "stock", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
private List<StockChartPoint> chart;
@OneToOne(mappedBy = "stock", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
private StockDetails details;
public void setDetails(StockDetails d)
{
details = d;
details.setStock(this);
}
}
和
@Getter
@Setter
@NoArgsConstructor
@Entity
@Table(name = "stock_details")
public class StockDetails
{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@OneToOne
@JoinColumn(name = "stock")
private Stock stock;
}
当details表为空时,在db中插入一个新值(和相应的行)就可以正常工作,我可以看到hibernate只记录一个intert语句。我的代码如下所示:
dbService.transactional(session ->
{
var stocks = getAllStocks();
var s = stocks.get(0);
StockDetails n = new StockDetails();
s.setDetails(n);
session.save(s);
});
这个 DbService.transactional
方法( TransactionExecutor
只是一个功能接口):
public void transactional(TransactionExecutor executor)
{
Transaction t = null;
try
{
t = session.beginTransaction();
executor.execute(session);
session.flush();
t.commit();
}
catch (HibernateException ex)
{
if (t != null)
t.rollback();
}
}
但当明细表中已有一行时,将删除旧行并插入新行。结果是,每次更新值时,details表的pk都在增加。有没有我找不到的模式来解决这个问题?我也可以只更新现有的 Stock.details
但是这会导致把所有的字段从对象a复制到对象b,我想有一个更聪明的方法。我试着用 EntityManager
, merge()/saveOrUpdate()/persist()
而不是 save()
以及在新对象中操作id,但这导致什么也不更改或引发异常。
然后我遇到了另一个问题,我不知道是否与第一个问题有关:执行 dbService.transactional(...)
块两次它的行为不同:现在两行被添加到数据库中。sql日志如下所示:
...
Hibernate: insert into stock_details (stock) values (?)
Hibernate: delete from stock_details where id=?
-> insert/delete produces by the first run
Hibernate: select stock0_.id as id1_3_, stock0_.isin as isin2_3_, stock0_.name as name3_3_,
stock0_.symbol as symbol4_3_ from stocks stock0_
Hibernate: insert into stock_details (stock) values (?)
-> Just insert, no delete
如果需要更多的信息,请告诉我。
mysql.mysql-connector-java>8.0.22
org.hibernate.hibernate-core>5.4.25.final
org.hibernate.hibernate-validator>5.4.3.final
2条答案
按热度按时间o4tp2gmn1#
但当明细表中已有一行时,旧行将被删除。。。
…这是我们所期待的
orphanRemoval = true
…并插入新行…这显然也是意料之中的,因为您正在覆盖现有的
StockDetails
与s
一个全新的例子StockDetails
.如果要更新现有
StockDetails
,而不是创建新的StockDetails
实体,你需要,好吧,用java代码来做。我也可以只更新现有的stock.details字段,但这会导致将所有字段从对象a复制到对象b。。。
这将是最不容易出错的方法
……但我想有更聪明的方法
你可以这样做:
tuwxkamq2#
你可能已经猜到了,你实际上是在创建一个新的
StockDetails
把旧的换掉。如果要更新现有StockDetails
您真的需要获取它并逐字段更新它(正如您所说的那样)。就像:为了防止逐字段复制,您可以获得
StockDetails
并将可能的编辑直接放入数据库中,这样就不需要复制每个字段。但是,如果您需要将某些dto中的值写入实体,则可以使用一些库来减轻复制的痛苦。
我只想提一个
ModelMapper
. 复制过程如下:哪里
stockDetailsDto
是某个dto对象,其中包含要更新到实体的字段。如果你的
StockDetails
包含所有其他字段,甚至包括未更改的字段em.merge
就像克里兹的回答中所说的,这是最简单的。但是如果只得到更新的字段,那么其他字段将设置为null。有时id是不可设置的,然后合并是不可能的。