如何用Hibernate+MySQL编写查询获取BER的单条合并数据?
首先得说下你原来的MySQL语句为什么不对:你用逗号直接连接三个表,没有指定任何关联条件,这会触发笛卡尔积——也就是把每个表中符合code='BER'的记录进行全组合,结果自然不是你想要的合并成一行的效果。我们先把SQL逻辑捋顺,再用Hibernate来实现。
1. 先修正MySQL查询逻辑
假设每个表最多只有一条code='BER'的记录,要把三个表的对应信息合并成一行,可以用LEFT JOIN来关联三个表(因为三个表之间没有直接关联字段,所以用1=1作为连接条件,确保每个表的BER记录都能拼到一行里):
SELECT f.code AS finnair_code, f.airportname AS finnair_name, f.latitude AS finnair_lat, f.longitude AS finnair_lon, l.code AS lh_code, l.airportname AS lh_name, l.latitude AS lh_lat, l.longitude AS lh_lon, s.code AS sas_code, s.airportname AS sas_name, s.latitude AS sas_lat, s.longitude AS sas_lon FROM (SELECT * FROM frs.finnairroute WHERE code = 'BER') f LEFT JOIN (SELECT * FROM frs.lhroute WHERE code = 'BER') l ON 1=1 LEFT JOIN (SELECT * FROM frs.sasroute WHERE code = 'BER') s ON 1=1;
如果某个表没有BER的记录,对应的列会显示NULL,完全符合你的需求。
2. Hibernate实现方式
Hibernate支持多种查询方式,这里给你两种常用的方案:
方案一:原生SQL查询(最直接)
因为我们已经有了正确的SQL语句,直接用Hibernate执行原生SQL是最快的方式。不过因为查询结果不是对应单一实体类,我们需要先定义一个DTO来接收结果:
// 自定义DTO,用来接收合并后的查询结果 public class AirportCombinedDTO { private String finnairCode; private String finnairName; private Double finnairLat; private Double finnairLon; private String lhCode; private String lhName; private Double lhLat; private Double lhLon; private String sasCode; private String sasName; private Double sasLat; private Double sasLon; // 注意:构造方法的参数顺序要和SQL查询结果的列顺序完全对应 public AirportCombinedDTO(String finnairCode, String finnairName, Double finnairLat, Double finnairLon, String lhCode, String lhName, Double lhLat, Double lhLon, String sasCode, String sasName, Double sasLat, Double sasLon) { this.finnairCode = finnairCode; this.finnairName = finnairName; this.finnairLat = finnairLat; this.finnairLon = finnairLon; this.lhCode = lhCode; this.lhName = lhName; this.lhLat = lhLat; this.lhLon = lhLon; this.sasCode = sasCode; this.sasName = sasName; this.sasLat = sasLat; this.sasLon = sasLon; } // 别忘了添加Getter和Setter方法 }
然后在你的DAO/Service层执行原生SQL:
public AirportCombinedDTO getCombinedBERAirport() { String sql = """ SELECT f.code AS finnair_code, f.airportname AS finnair_name, f.latitude AS finnair_lat, f.longitude AS finnair_lon, l.code AS lh_code, l.airportname AS lh_name, l.latitude AS lh_lat, l.longitude AS lh_lon, s.code AS sas_code, s.airportname AS sas_name, s.latitude AS sas_lat, s.longitude AS sas_lon FROM (SELECT * FROM frs.finnairroute WHERE code = 'BER') f LEFT JOIN (SELECT * FROM frs.lhroute WHERE code = 'BER') l ON 1=1 LEFT JOIN (SELECT * FROM frs.sasroute WHERE code = 'BER') s ON 1=1 """; EntityManager em = getEntityManager(); // 这里根据你的项目配置获取EntityManager,比如Spring注入 Query query = em.createNativeQuery(sql, AirportCombinedDTO.class); // 如果确定只有一行结果,用getSingleResult;如果可能没有,用getResultList().stream().findFirst().orElse(null) return (AirportCombinedDTO) query.getSingleResult(); }
要是你的Hibernate版本比较旧,可能需要用@SqlResultSetMapping来显式映射结果到DTO,或者手动处理查询结果。
方案二:HQL查询(基于实体类)
如果你已经为三个表创建了对应的实体类(比如FinnairRoute、LhRoute、SasRoute,每个类对应表的四个字段),可以用HQL来实现,更贴合ORM的风格:
public AirportCombinedDTO getCombinedBERAirportViaHQL() { String hql = """ SELECT NEW com.yourpackage.AirportCombinedDTO( f.code, f.airportname, f.latitude, f.longitude, l.code, l.airportname, l.latitude, l.longitude, s.code, s.airportname, s.latitude, s.longitude ) FROM FinnairRoute f LEFT JOIN LhRoute l ON l.code = 'BER' LEFT JOIN SasRoute s ON s.code = 'BER' WHERE f.code = 'BER' """; EntityManager em = getEntityManager(); Query query = em.createQuery(hql, AirportCombinedDTO.class); return (AirportCombinedDTO) query.getSingleResult(); }
这个方案需要确保实体类和数据库表的映射完全正确,HQL语法也符合要求。如果某个表没有BER记录,对应的字段会是null。
补充小提示
- 如果业务上允许某个表存在多条
code='BER'的记录,那你需要调整逻辑,比如在子查询里加LIMIT 1或者用GROUP BY取第一条。 - 要是你不需要合并成一行,而是要把三个表中
BER的记录分别列出来(每行对应一个表的记录),可以用UNION ALL:
SELECT code, airportname, latitude, longitude, 'finnair' AS source FROM frs.finnairroute WHERE code='BER' UNION ALL SELECT code, airportname, latitude, longitude, 'lh' AS source FROM frs.lhroute WHERE code='BER' UNION ALL SELECT code, airportname, latitude, longitude, 'sas' AS source FROM frs.sasroute WHERE code='BER'
对应的Hibernate实现也可以用原生SQL,或者HQL的UNION(Hibernate 5.1及以上版本支持)。
内容的提问来源于stack exchange,提问作者Kkr0319

