如何使用Hibernate获取关联实体

时间:2015-07-09 07:30:11

标签: java spring hibernate jpa orm

我正在使用Spring rest和Hibernate开发一个应用程序,我希望从数据库中获取嵌套记录,就像我Profession得到User一样,现在我想要获取Users与之前提取的Profession相关联。

这是我的Dao课程

@SuppressWarnings({ "unchecked", "rawtypes" })
public List<Profession> getProfessionById(long id) throws Exception {
    session = sessionFactory.openSession();
    Criteria cr = session.createCriteria(Profession.class);
    cr.add(Restrictions.eq("uid", id));
    List results = cr.list();
    tx = session.getTransaction();
    session.beginTransaction();
    tx.commit();
    return results;
}

7 个答案:

答案 0 :(得分:2)

提取策略

有四种提取策略

  1. fetch-“join”=禁用延迟加载,始终加载所有集合和实体。
  2. fetch-“select”(默认)=延迟加载所有集合和实体。
  3. batch-size =“N”=获取“N”个集合或实体,不记录
  4. fetch-“subselect”=将其集合分组为子选择语句。
  5. 有关详细说明,您可以查看Hibernate文档。

    FetchType.LAZY是按需

    FetchType.EAGER立即

    @SuppressWarnings({ "unchecked", "rawtypes" })
    public List<User> getProfessionById(long id) throws Exception {
       session = sessionFactory.openSession();
           Criteria cr = session.createCriteria(Profession.class, "pro")
                                 .setFetchMode("user", FetchMode.JOIN);
            cr.add( Restrictions.eq("uid", id));
            Profession pro = cr.uniqueResult();
            tx = session.getTransaction();
            session.beginTransaction();
            tx.commit();
        return pro.getUsers();
    }  
    

答案 1 :(得分:2)

根据您的查询,Profession表有一个uid列,可能是Users表的FK,我认为Users表应该有一个FK代替专业

因此Users表格与many-to-one的关联Profession

@ManyToOne
private Profession profession;

并且Profession可以关联具有该特定职业的所有用户,因此在Profession实体中您具有此关联的反面:

@OneToMany(mappedBy = "profession")
private List<Users> users = new ArrayList<>();

现在要让所有用户都拥有专业,您可以运行如下的简单查询:

List<Users> users = (ist<Users>) session.createQuery(
    "select u " +
    "from Profession p " +
    "join fetch p.users u " +
    "where p.id = :id")
.setParameter("id", proffesionId)
.list();

答案 2 :(得分:2)

首先,您需要在Profession和User实体类

中添加以下映射

在专业课

//bi-directional many-to-one association to Users
@OneToMany(mappedBy="profession", cascade=CascadeType.ALL)
private List<User> users;

public List<User> getUsers() {
    return this.users;
}

public void setUsers(List<User> users) {
    this.users = users;
}

在用户实体类

@ManyToOne(fetch=FetchType.EAGER)
@JoinColumn(name="profession_id")
private Profession profession;

然后,您可以使用现有的DAO类代码通过id获取专业对象。

@SuppressWarnings({ "unchecked", "rawtypes" })
public List<Profession> getProfessionById(long id) throws Exception {
    session = sessionFactory.openSession();
    Profession profession =  (Profession) session.get(Profession.class, id);

    // here you can get list of users for this profession 
    // List<User> users = profession.getUsers();
    return profession;
}

答案 3 :(得分:2)

我认为你在DAO类中做错了,因为你在方法中打开了一个会话和一个事务,并且可能在每个DAO方法中。

执行此操作会带来性能成本,并且在概念上很糟糕,事务必须将一组完全成功或失败的操作分组。在大多数情况下,数据库事务将与整个业务操作相关,在这种情况下是REST请求。像

这样的东西
  

POST / user

     

@ RestController.createUser

     

开启交易

     

UserDAO.saveUser

     

提交交易

     

响应

另外,如果您查看代码,则表示您正在打开一个事务,然后进行克隆。

tx = session.getTransaction();
session.beginTransaction();
tx.commit();

在这种情况下,您正在查询数据库,因此根本不需要事务。

事务是您的应用程序中的一个交叉问题,并且鉴于您已经在使用Spring,您应该查看@Transactional注释(或其xml等效项)以实现与AOP的事务性(Spring创建一个方面)。这将使您的代码更具可读性和可维护性。

@ManojP的答案有好事和坏事。我认为你应该尽可能避免双向关系,因为它会使设计变得更难。我的建议是:始终使用单向关系,如果发现无法避免的情况,请使用它。好消息是,当他这样做时,会向你展示懒惰的使用:

List<User> users = profession.getUsers();

这行代码应该在DAO之外。会发生的是,您有一个标记为惰性的用户列表(默认值),然后当您使用条件查询获取专业时,将触发表专业的选择,并且每个专业对象都使用代理集合而不是真正的集合。当你调用profession.getUsers()时,一个新的选择在用户表上被trigerred,其中profession_id = profession.getId()。所以:

  1. 列出结果= criteria.list();
  2. 从职业选择*
  3. professionA.getUsers();
  4. 从User中选择*,其中profession_id =:professionalA.getId()
  5. 但要注意!如果你有一系列的专业(比如我认为你有,因为你正在返回一个名单)并迭代每个专业,并询问你将要做的用户列表:

    1. 列出结果= criteria.list();
    2. 从职业选择*
    3. 为每个职业 - &gt; profession.getUsers
    4. 从User中选择*,其中profession_id =:professionalA.getId()
    5. 从User中选择*,其中profession_id =:professionB.getId()
    6. 这会有不好的表现。

      @farvilain的答案很好。但在这种情况下,您将始终使用它的Users集合检索Profession,因为在您的DAO中,您始终使用FetchMode.JOIN,然后您将失去懒惰的好处。因此,如果您在查询专业时总是需要用户列表,请对用户集合使用lazy = true,但要注意可能产生的成本(如果用户在查询专业时有非惰性集合A)您还将检索用户列表和集合A)。 如果您不想这样,也许您可​​以签名:

      public List<Profession> getProfessionById(Long id, FetchMode fetchMode) throws Exception 
      

      这不是很好,但表明DAO客户端可以选择fecth模式。

答案 4 :(得分:1)

您可以使用以下内容:

Criteria cr = session.createCriteria(Profession.class);
cr.setFetchMode("user", FetchMode.EAGER);
cr.add(Restrictions.eq("uid", id));
List results = cr.list();

我还没有使用过Hibernate标准,但是快速谷歌提出了类似的东西。

答案 5 :(得分:1)

从您的代码开始

@SuppressWarnings({ "unchecked", "rawtypes" })
public List<Profession> getProfessionById(long id) throws Exception {
    session = sessionFactory.openSession();
    Criteria cr = session.createCriteria(Profession.class);
    cr.add(Restrictions.eq("uid", id));
    List results = cr.list();
    tx = session.getTransaction();
    session.beginTransaction();
    tx.commit();
    return results;
}

如果您使用Spring,我首先建议您使用Transactionnal注释。它使代码更清晰。然后停止使用SuppressWarnings,它是uggly和无用的,你可以配置你的IDE来隐藏它们。

@Transactionnal(readonly = true)
public List<Profession> getProfessionById(long id) throws Exception {
    Criteria cr = session.createCriteria(Profession.class);
    cr.add(Restrictions.eq("uid", id));
    List results = cr.list();
    return results;
}

没有使用标准是流畅的API并且不创建局部变量的事实,现在它没有用。

@Transactionnal(readonly = true)
public List<Profession> getProfessionById(long id) throws Exception {
    return session
        .createCriteria(Profession.class)
        .add(Restrictions.eq("uid", id))
        .list();
}

让我们现在解决您的问题

@Transactionnal(readonly = true)
public List<Profession> getProfessionById(long id) throws Exception {
    return session
        .createCriteria(Profession.class)
        .add(Restrictions.eq("uid", id))
        .setFetchMode("users", FetchMode.JOIN)
        .list();
}

当然,这需要专业的这一行;

public class Profession {
    [...]

    @OneToMany(mappedBy = "profession")
    private Set<Users> users = new HashSet<>();

    [...]
}

警告

你不需要getter / setter,不需要时不要调用反向映射。

一定不要使用List,Hibernate不喜欢没有订单声明的List。

不要在实体中急切地将用户声明为抓取,每次加载很多用户拥有的专业时,你都会遇到可怕的问题,即使你不善于自己获取它们。

不要使用FetchMode.EAGER,不推荐使用它!

答案 6 :(得分:0)

使用ManyToMany映射来获取专业,以便已经过来的用户也会来。检查此link