能否通过Java Hibernate执行UNION查询合并两张数据表数据?
在Hibernate中合并同结构两张表数据到DTO列表的解决方案
原方案不可行的原因
- HQL的
UNION要求查询返回的结果类型必须完全一致,但Table和Table2是两个独立的实体类,直接执行select a from Table a union select a from Table2 a会抛出类型不匹配异常 - 代码错误地将查询结果当作
Object[]处理:如果查询的是实体对象,返回的是实体实例列表而非数组,会触发类型转换错误 - 循环中
dto对象仅初始化一次,后续赋值会覆盖之前的内容,最终列表里的所有元素都指向同一个对象,数据完全重复
可行实现方案
方案一:HQL查询字段直接映射到DTO
直接查询两张表的字段,避免实体类型不匹配问题,同时修正DTO初始化逻辑:
- 调整HQL语句,指定要查询的字段
select a.name, a.phoneNumber from Table a union select b.name, b.phoneNumber from Table2 b
- 修正数据转换代码
public ArrayList<DTO> getUsers(){ ArrayList<DTO> flist = new ArrayList<>(); String query = "select a.name, a.phoneNumber from Table a union select b.name, b.phoneNumber from Table2 b"; try{ AppEntityManager appEntMgr = AppEntityManager.getAppEntityManager(); appEntMgr.startTransaction(); Query q = appEntMgr.createQuery(query); q.setFirstResult(0); q.setMaxResults(10); List<Object[]> list = q.getResultList(); for (Object[] row : list) { DTO dto = new DTO(); // 每次循环新建DTO实例 dto.setName((String)row[0]); dto.setPhoneNumber((String)row[1]); flist.add(dto); } }catch (Exception e) { log.error("Exception ::" + e); } return flist; }
方案二:基于父类的多态查询
通过公共父类统一实体类型,支持HQL的UNION查询:
- 创建映射父类,提取两张表的公共字段
@MappedSuperclass public abstract class BaseTable { @Id @Column(name="NAME") private String name; @Column(name="PHONE") private String phoneNumber; // 公共getter/setter public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPhoneNumber() { return phoneNumber; } public void setPhoneNumber(String phoneNumber) { this.phoneNumber = phoneNumber; } }
- 修改实体类继承父类
@Entity @Table(name="TABLE") public class Table extends BaseTable { // 无需重复定义字段,继承父类即可 }
@Entity @Table(name="TABLE_2") public class Table2 extends BaseTable { // 无需重复定义字段,继承父类即可 }
- 使用父类进行HQL查询并转换DTO
public ArrayList<DTO> getUsers(){ ArrayList<DTO> flist = new ArrayList<>(); String query = "select a from BaseTable a union select b from BaseTable b"; try{ AppEntityManager appEntMgr = AppEntityManager.getAppEntityManager(); appEntMgr.startTransaction(); Query q = appEntMgr.createQuery(query); q.setFirstResult(0); q.setMaxResults(10); List<BaseTable> list = q.getResultList(); for (BaseTable entity : list) { DTO dto = new DTO(); dto.setName(entity.getName()); dto.setPhoneNumber(entity.getPhoneNumber()); flist.add(dto); } }catch (Exception e) { log.error("Exception ::" + e); } return flist; }
方案三:原生SQL查询
如果HQL的限制较多,可直接使用原生SQL绕过实体类型限制:
public ArrayList<DTO> getUsers(){ ArrayList<DTO> flist = new ArrayList<>(); String sql = "SELECT NAME, PHONE FROM TABLE UNION SELECT NAME, PHONE FROM TABLE_2"; try{ AppEntityManager appEntMgr = AppEntityManager.getAppEntityManager(); appEntMgr.startTransaction(); Query q = appEntMgr.createNativeQuery(sql); q.setFirstResult(0); q.setMaxResults(10); List<Object[]> list = q.getResultList(); for (Object[] row : list) { DTO dto = new DTO(); dto.setName((String)row[0]); dto.setPhoneNumber((String)row[1]); flist.add(dto); } }catch (Exception e) { log.error("Exception ::" + e); } return flist; }
内容的提问来源于stack exchange,提问作者landa
相关产品推荐
相关产品推荐

