Help with hibernate to JPA composite key mapping

I am working on migrating HibernateAlertDAO from hibernate to JPA but running into issues with one migration that I can’t seem to get working.

The original hibernate code is

		Criteria crit = sessionFactory.getCurrentSession().createCriteria(Alert.class, "alert");

		if (user != null && user.getUserId() != null) {
			crit.createCriteria("recipients", "recipient");
			crit.add(Restrictions.eq("recipient.recipient", user));

And I have migrated with

		Session session = sessionFactory.getCurrentSession();
		CriteriaBuilder cb = session.getCriteriaBuilder();
		CriteriaQuery<Alert> cq = cb.createQuery(Alert.class);
		Root<Alert> root = cq.from(Alert.class);

		List<Predicate> predicates = new ArrayList<>();

		if (user != null && user.getUserId() != null) {
			predicates.add(cb.equal(root.join("recipients").get("recipient"), user));

I get the error

java.lang.IllegalArgumentException: Unable to locate Attribute with the the given name [recipient] on this ManagedType [org.openmrs.notification.AlertRecipient]

I think this might be something to do with the composite key in the hbm.xml

Anyone got any ideas on how to translate this to JPA?

i would go with predicates.add(cb.equal(root.join("recipients").get("recipientId"), user.getUserId()));

This is because the persisted fields from AlertRecipient, recipientId == user_id which is literially the same with recipient == user_id though this timeround it is composite so we cant fetch it (correct me if we can) but we can uget its value by fetching recipientId.

That’s what i tried here (below) and the test passed:

public List<Alert> getAlerts(User user, boolean includeRead, boolean includeExpired) throws DAOException {
	log.debug("Getting alerts for user " + user + " read? " + includeRead + " expired? " + includeExpired);
	
	Session session = sessionFactory.getCurrentSession();
	CriteriaBuilder cb = session.getCriteriaBuilder();
	CriteriaQuery<Alert> cq = cb.createQuery(Alert.class);
	Root<Alert> root = cq.from(Alert.class);
	
	List<Predicate> predicates = new ArrayList<>();
	
	if (user != null && user.getUserId() != null) {
		predicates.add(cb.equal(root.join("recipients").get("recipientId"), user.getUserId()));
	} else {
		// getting here means we passed in no user or a blank user.
		// a null recipient column means get stuff for the anonymous user
		
		// returning an empty list for now because the above throws an error.
		// we may need to remodel how recipients are handled to get anonymous users alerts
		return Collections.emptyList();
	}
	
	// exclude the expired alerts unless requested
	if (!includeExpired) {
		predicates.add(cb.or(cb.isNull(root.get("dateToExpire")), cb.greaterThan(root.get("dateToExpire"), new Date())));
	}
	
	// exclude the read alerts unless requested
	if (!includeRead && user.getUserId() != null) {
		predicates.add(cb.equal(root.get("alertRead"), false));
		predicates.add(cb.equal(root.get("recipient.alertRead"), false));
	}
	
	cq.orderBy(cb.desc(root.get("dateChanged"))).where(predicates.toArray(new Predicate[]{}));
	return session.createQuery(cq).getResultList();
}
2 Likes

Ah yes that worked! Thank you very much for your help :slight_smile: