我正在使用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;
}
答案 0 :(得分:2)
提取策略
有四种提取策略
有关详细说明,您可以查看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()。所以:
但要注意!如果你有一系列的专业(比如我认为你有,因为你正在返回一个名单)并迭代每个专业,并询问你将要做的用户列表:
这会有不好的表现。
@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。