JPA坚持ManyToOne

时间:2014-11-18 18:17:41

标签: hibernate jpa

这看起来非常简单,但我找不到快速解决方案。

我有一个非常简单的关系。我有一个USER表,包含三列,一个生成的ID,一个VARCHAR名称,以及一个带有两列的HOMETOWN表的外键 - 生成的ID和VARCHAR值。超级简单。

所以我在UserEntity中假设一些典型的东西:

  @ManyToOne
  @JoinColumn(name = "HOMETOWN_ID")
  private Hometown hometown;

问题是新用户通过{“name”:“William Burroughs”,“hometown”:“Portland,OR”}以json的形式出现在我的UserEntity上我真正想要的是#setHometown(String)方法。

我唯一的问题是,我的UserEntity似乎需要一个EntityManager实例,并且setHometown方法需要是事务性的,看看是否存在“Portland,OR”的Hometown,如果不存在则持久化,然后将this.hometown设置为我发现或持久的内容。你看,“Portland,OR”已经或者可能不存在于HOMETOWN中。

这可能是对的吗?对于这么简单的事情来说,似乎有很多工作,我更喜欢我的实体不需要EntityManager的实例。

我觉得我正在努力使它变得比它需要的更难并且缺少超级简单的解决方案。还要注意,它只是一个String,我可以创建一个外键引用。

如果您需要更多数据,请告知我们,我会更新此帖子。

更新

我想困扰我的是,如果用户的客户不必知道#setHometown的实现是否将字符串存储到USER中的列或者引用HOMETOWN中的行,我更愿意。如果我以模式方式更改所有使用UserEntry的代码必须更改。我想这将是使用隐藏JPA和DAO相关内容的DTO / POJO和服务层的另一个论点。您认为JPA会抽象掉简单关系和这些常见模式,以便对象中的String字段可以迁移到表列或外键引用。

使用SQL这将是一个简单的存储过程,以INSERT开始... RETURNING和SELECT if error - 全部封装在单个存储过程调用中。使用JPA很难保护代码客户端不受我的实现选择的影响,我必须做类似的事情:

@Transactional
public void save(User user, String name) {
    Hometown hometown = new Hometown(name);
    try {
        saveHometown(hometown);
    }
    catch (Exception e) {
        TypedQuery<Hometown> query = getEntityManager().createQuery("SELECT h FROM Hometown h WHERE h.name = :name", Hometown.class);
        query.setParameter("name", name);
        hometown = query.getSingleResult();
    }

    user.setHometown(hometown);
    getEntityManager().persist(user);
}

@Transactional(propagation = Propagation.REQUIRES_NEW)
protected void saveHometown(final Hometown hometown) {
    getEntityManager().persist(hometown);
}

对于像这样的简单关系来说,这很复杂。如果我想先查询并坚持,如果它不存在那么我必须在循环中执行或创建表锁。真?这是@ManyToOne关系中最简单的方法,其中外键可能存在也可能不存在?对于City,State等,你可以拥有大量的这些。好吧,我想这就是生活。但我发誓一定有一个简单的方法,不是吗?

1 个答案:

答案 0 :(得分:0)

好吧,你可以简化你的代码,有类似的东西

@Transactional
public void save(User user, String name) {
    Hometown hometown = getEntityManager().createQuery("SELECT h FROM Hometown h WHERE h.name = :name", Hometown.class).setParameter("name", name).getSingleResult();
    if (hometown == null) {
        hometown = new Hometown(name);
    }

    user.setHometown(hometown);
    getEntityManager().persist(user);
}

而且,您只需要将cascadeUser设置为Hometown

@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "HOMETOWN_ID")
private Hometown hometown;

对于这些情况,我们通常使用findOrCreate方法返回现有实体,如果不存在则创建新实体。然后在需要的地方重复使用它们。

public Hometown findOrCreateHometown(String name) {
    Hometown hometown = getEntityManager().createQuery("SELECT h FROM Hometown h WHERE h.name = :name", Hometown.class).setParameter("name", name).getSingleResult();
    if (hometown == null) {
        hometown = new Hometown(name);
    }
    return hometown;
}

你的save()方法看起来像这样

@Transactional
public void save(User user, String name) {
    Hometown hometown = findOrCreateHometown(name);

    user.setHometown(hometown);
    getEntityManager().persist(user);
}