如何使用Hibernate映射代表集合的独立实体?
独立集合类的Hibernate映射解决方案
问题背景
现有两个数据库表,结构及数据如下:
事件列表表
+---------------------+-------------------------+-------------+---------------------+ | date | name | description | notification_needed | +---------------------+-------------------------+-------------+---------------------+ | 2024-08-25 00:00:00 | It`s 25-th of August! | NULL | 1 | | 2024-08-27 00:00:00 | It`s 27-th of August! | NULL | 1 | | 2024-09-03 00:00:00 | It`s 3-rd of September! | NULL | 1 | +---------------------+-------------------------+-------------+---------------------+
用户队列表(关联Users表)
+----------+-----------------------+---------+------------+ | position | username | status | first_name | +----------+-----------------------+---------+------------+ | 2 | tg_touch_purple | ADMIN | Eugene | | 3 | tg_hands_off_the_corn | REGULAR | Carl | | 1 | tg_glassk | REGULAR | Joe | +----------+-----------------------+---------+------------+
需求是将这两个表分别映射为HashMap<LocalDate, Event>和LinkedList<User>,但这些集合是独立实体,无法用@OneToMany这类常规集合注解。之前尝试的方案要么逻辑不合理,要么需要手动转换集合;原生查询不符合Hibernate风格,且无参数的createNativeQuery已废弃,需要通用、自动化的加载方式。
可行的通用自动化方案
1. 自定义ResultTransformer直接转集合(Hibernate原生方案)
通过实现Hibernate的ResultTransformer,直接把查询结果转换成目标集合类型,无需额外手动转换:
转HashMap<LocalDate, Event>的转换器
public class EventHashMapTransformer implements ResultTransformer { @Override public Object transformTuple(Object[] tuple, String[] aliases) { // 把查询出的Event对象和它的date封装成键值对 Event event = (Event) tuple[0]; return new AbstractMap.SimpleEntry<>(event.getDate(), event); } @Override public List transformList(List collection) { // 把键值对列表转成HashMap HashMap<LocalDate, Event> resultMap = new HashMap<>(); for (Object entry : collection) { AbstractMap.SimpleEntry<LocalDate, Event> mapEntry = (AbstractMap.SimpleEntry<LocalDate, Event>) entry; resultMap.put(mapEntry.getKey(), mapEntry.getValue()); } return Collections.singletonList(resultMap); } }
使用示例:
Session session = sessionFactory.getCurrentSession(); Query query = session.createQuery("FROM Event e"); query.setResultTransformer(new EventHashMapTransformer()); HashMap<LocalDate, Event> eventMap = (HashMap<LocalDate, Event>) query.uniqueResult();
转LinkedList<User>的转换器
按position排序后直接封装成LinkedList:
public class UserLinkedListTransformer implements ResultTransformer { @Override public Object transformTuple(Object[] tuple, String[] aliases) { return tuple[0]; // 直接返回查询到的User对象 } @Override public List transformList(List collection) { return new LinkedList<>(collection); // 把查询结果列表转成LinkedList } }
使用示例:
Session session = sessionFactory.getCurrentSession(); Query query = session.createQuery("FROM User u ORDER BY u.position ASC"); query.setResultTransformer(new UserLinkedListTransformer()); LinkedList<User> userQueue = (LinkedList<User>) query.uniqueResult();
2. Spring Data JPA自定义Repository实现(Spring环境下)
如果用Spring Data JPA,可以通过自定义Repository实现类,在内部完成集合转换,对外直接返回目标集合类型:
事件Repository实现
// 基础Repository接口 public interface EventRepository extends JpaRepository<Event, Long>, EventRepositoryCustom { } // 自定义方法接口 public interface EventRepositoryCustom { HashMap<LocalDate, Event> findAllAsMap(); } // 自定义方法实现类 public class EventRepositoryImpl implements EventRepositoryCustom { @PersistenceContext private EntityManager entityManager; @Override public HashMap<LocalDate, Event> findAllAsMap() { List<Event> events = entityManager.createQuery("FROM Event", Event.class).getResultList(); HashMap<LocalDate, Event> eventMap = new HashMap<>(); events.forEach(event -> eventMap.put(event.getDate(), event)); return eventMap; } }
用户队列Repository实现
// 基础Repository接口 public interface UserRepository extends JpaRepository<User, Long>, UserRepositoryCustom { } // 自定义方法接口 public interface UserRepositoryCustom { LinkedList<User> findAllAsQueue(); } // 自定义方法实现类 public class UserRepositoryImpl implements UserRepositoryCustom { @PersistenceContext private EntityManager entityManager; @Override public LinkedList<User> findAllAsQueue() { List<User> users = entityManager.createQuery("FROM User ORDER BY position ASC", User.class).getResultList(); return new LinkedList<>(users); } }
3. 自定义Hibernate集合类型(进阶复用方案)
通过实现Hibernate的CollectionType,把目标集合类型注册为Hibernate支持的集合类型,后续可以直接通过映射配置使用,更贴近Hibernate原生风格:
public class EventHashMapType extends AbstractCollectionType { public EventHashMapType(TypeFactory.TypeScope typeScope) { super(typeScope, HashMap.class); } @Override public Collection instantiate(int anticipatedSize) { return new HashMap<>(anticipatedSize); } @Override public Iterator getElementsIterator(Collection collection) { return ((HashMap<?, ?>) collection).values().iterator(); } @Override public boolean contains(Object collection, Object element) { Event event = (Event) element; return ((HashMap<LocalDate, Event>) collection).containsKey(event.getDate()); } @Override public Object indexOf(Object collection, Object element) { Event event = (Event) element; return event.getDate(); } }
注册该类型后,就可以在Hibernate映射文件或注解中指定使用这个集合类型,适合需要长期复用该集合映射逻辑的场景。
内容的提问来源于stack exchange,提问作者probox36
相关产品推荐
相关产品推荐

