1

我正在尝试通过 DAO 对象执行数据库访问,并且遇到了需要查询另一个实体中的字段的情况。

考虑通过外键在实体 A 中连接的两个实体(实体 A 和实体 B)EntityA.idEntityB

我有GenericDao<EntityA> daoA并且我正在尝试获取与 EntityB 的确定字段匹配的所有结果:idEntityB.fieldOfB所有结果都在 dao 的相同查找方法中。

可能吗?如果是这样,一些方向会很好。谢谢

编辑

我的代码示例:

实体

public class EntityA {
    @JoinColumn(name = "id_entity_b", referencedColumnName = "id")
    @ManyToOne(optional = false, fetch = FetchType.EAGER)
    private EntityB idEntityB;
    // getter+setter...
}

public class EntityB {
    // ...
    private String fieldOfB;
    // getter+setter...
}

DAO 访问

GenericDao<EntityA> daoA = // ...

Set<Criterion> filter = new HashSet<Criterion>();
filter.add(Restrictions.eq("idEntityB.fieldOfB"));

List<EntityA> list = dao.findByFilter(filter);

错误消息类似于“无法解析属性 idEntityB.fieldOfB

编辑 2

我能够找到我想做的事情。尽管我的 API 略有不同,但我相信这对于在自己项目的早期阶段遇到此问题的任何人都有帮助。

http://code.google.com/p/hibernate-generic-dao/

该框架具有强大而灵活的搜索功能。这是通过将搜索对象传递给通用和通用 DAO 上的搜索方法来使用的。

此项目完全支持具有嵌套属性的搜索。

4

2 回答 2

3

这是我的通用标准过滤方法。

根据 bean 约定的属性具有以下形式foo.bar.name
使用 Criteria API,可以从给定的过滤映射构建树,并且可以添加限制。我在测试期间观察到的一个特殊情况是,对标识符属性进行过滤不需要新的子标准,因为该属性已经被获取。

/**
 * Creates a detached criteria from the given Type and given map of filters.
 * 
 * @param type Target type the Criteria is build for.
 * @param identifierPropertyName If provided (not null) the identifier
 *            property name can be identified for the given type to simplify
 *            the queries if the identifier property is the only property
 *            used on the parent no subqueries are needed.
 * @param filters
 * 
 * @see #createTree(Set, String)
 * @see #addRestrictions(DetachedCriteria, TreeNode)
 * 
 * @return
 */
public static DetachedCriteria createDetachedCriteria(final Class<?> type, final String identifierPropertyName,
    final Map<String, Criterion> filters)
{
    final DetachedCriteria criteria = DetachedCriteria.forClass(type);

    // add restrictions using tree
    final TreeNode<Entry<String, Criterion>> rootNode = HibernateUtils2.createTree(filters.entrySet(),
        identifierPropertyName);

    final Iterator<TreeNode<Entry<String, Criterion>>> it = rootNode.getChildren().iterator();

    while (it.hasNext())
        HibernateUtils.addRestrictions(criteria, it.next());

    return criteria;
}

/**
 * Creates a Tree from the given Set using a fictional root TreeNode.
 * 
 * @param <T>
 * 
 * @param filters
 * @param identifierPropertyName Property name which is merged with its
 *            parent property. Example: <b>user.id</b> is treated as single
 *            property.
 * @return
 */
public static <T extends Object> TreeNode<Entry<String, T>> createTree(final Set<Entry<String, T>> filters,
    final String identifierPropertyName)
{

    final Iterator<Entry<String, Object>> it = filters.iterator();

    /*
     * create key property tree for Entity properties
     */
    final TreeNode<Entry<String, Object>> rootNode = new TreeNode<Entry<String, Object>>(
        new SimpleEntry<String, Object>("root", null));

    while (it.hasNext())
    {
        final Entry<String, Object> entry = it.next();
        // foo.bar.name
        final String key = entry.getKey();

        String[] props;

        /*
         * check if we have a nested hierarchy
         */
        if (key.contains("."))
        {
            props = key.split("\\.");
            // check for identifier since identifier property name does not
            // need new subcriteria
            if (!StringUtils.isBlank(identifierPropertyName))
            {
                int propsTempLength = props.length - 1;
                if (props[propsTempLength].equals(identifierPropertyName))
                {
                    props = Arrays.copyOf(props, propsTempLength);
                    propsTempLength--;
                    props[propsTempLength] = props[propsTempLength] + "." + identifierPropertyName;
                }
            }

            // check for "this" identifier of beginning, which needs to be
            // added for projections because of hibernate not recognizing it
            if (props.length > 1 && props[0].equals("this"))
            {
                props[0] = "this." + props[1];

                props = ArrayUtils.remove(props, 1);
            }
        }
        else
            props = new String[]
            {
                key
            };

        TreeNode<Entry<String, Object>> currNode = rootNode;

        // create nested criteria
        for (int i = 0; i < props.length; i++)
        {
            Object valueAdd;

            // only leaf needs value
            if (i != props.length - 1)
                valueAdd = null;
            else
                valueAdd = entry.getValue();

            final TreeNode<Entry<String, Object>> childTempNode = new TreeNode<Entry<String, Object>>(
                new SimpleEntry<String, Object>(props[i], valueAdd));

            // try to get the real node
            TreeNode<Entry<String, Object>> childNode = currNode.getChild(childTempNode.getElement());
            // check if we already have a unique node
            if (childNode == null)
            {
                childNode = childTempNode;
                // add new child to set if its a new node
                currNode.addChild(childNode);
            }

            currNode = childNode;
        }
    }

    return rootNode;
}

/**
 * Recursively adds the given Restriction's wrapped in the given TreeNode to
 * the Criteria.
 * 
 * @param criteria
 * @param treeNode
 */
public static void addRestrictions(final DetachedCriteria criteria,
    final TreeNode<Entry<String, Criterion>> treeNode)
{
    // if we have a leaf simply add restriction
    if (treeNode.getChildren().size() == 0)
        criteria.add(treeNode.getElement().getValue());
    else
    {
        // create new sub Criteria and iterate children's
        final DetachedCriteria subCriteria = criteria.createCriteria(treeNode.getElement().getKey());

        final Iterator<TreeNode<Entry<String, Criterion>>> it = treeNode.getChildren().iterator();

        while (it.hasNext())
            HibernateUtils.addRestrictions(subCriteria, it.next());
    }
}

/*
 * Utility classes
 */

/**
 * Generic TreeNode implementation with a Set to hold its children to only allow
 * unique children's.
 */
public class TreeNode<T>
{
    private final T element;

    private final Set<TreeNode<T>> childrens;

    public TreeNode(final T element)
    {
        if (element == null)
            throw new IllegalArgumentException("Element cannot be null");

        this.element = element;

        this.childrens = new HashSet<TreeNode<T>>();
    }

    public void addChildren(final TreeNode<T> children)
    {
        this.childrens.add(children);
    }

    /**
     * Retrieves the children which equals the given one.
     * 
     * @param children
     * @return If no children equals the given one returns null.
     */
    public TreeNode<T> getChildren(final TreeNode<T> children)
    {
        final Iterator<TreeNode<T>> it = this.childrens.iterator();

        TreeNode<T> next = null;

        while (it.hasNext())
        {
            next = it.next();
            if (next.equals(children))
                return next;
        }

        return null;
    }

    public T getElement()
    {
        return this.element;
    }

    public Set<TreeNode<T>> getChildrens()
    {
        return this.childrens;
    }

    /**
     * Checks if the element of this instance equals the one of the given
     * Object.
     */
    @Override
    public boolean equals(final Object obj)
    {
        if (this == obj)
            return true;

        if (obj != null && obj instanceof TreeNode)
        {
            final TreeNode<?> treeNode = (TreeNode<?>) obj;

            return this.element.equals(treeNode.element);
        }
        else
            return false;
    }

    @Override
    public int hashCode()
    {
        int hash = 1;
        hash = hash * 17 + this.element.hashCode();
        return hash;
    }
}

希望这对您有所帮助。或者看看你提到的通用 dao 项目。我知道这个项目并检查了它,但从未下载过。

使用这种方法,可以非常简单地创建查询,如下所示:

Map<String, Object> filters = new HashMap<String, Object>();
filters.put("foo.bar.name", Restrictions.like("name", "peter"));
filters.put("foo.test.id", Restrictions.eq("id", 2));

List<Class> data = HibernateUtils.createDetachedCriteria(Class, "get identifier from sessionFactory", filters).getExecutableCriteria(session).list();

这种将属性名称作为键添加到 Restrictions 中的奇怪方法与 Restrictions 没有属性名称的 getter 和 setter 的事实有关。

自定义过滤

我的真实应用程序使用类似的代码,不仅限于Criterion类。对于我的 Web 层,我创建了与 Restrictions api 等效的自定义过滤器,但客户端不再需要休眠 jar。

编辑

不支持跨非实体(如组件和复合 ID)的通用过滤。ClassMetadata它可以在无需反思的情况下轻松扩展以支持它们。如果需要代码,我可以提供。

于 2012-08-20T19:26:18.847 回答
1

看看这个例子:http: //viralpatel.net/blogs/hibernate-one-to-one-mapping-tutorial-using-annotation/

您需要将实体之间的关系声明为

public class EntityA {
    @JoinColumn(name = "id_entity_b")
    @ManyToOne(optional = false, fetch = FetchType.EAGER)
    private EntityB idEntityB;
    // getter+setter... and the rest
}

而不是Integer使用EntityB.

这就是 Hibernate 提供的ORM的要点:您不需要使用键,而是使用对象。将此表示转换为带有键的关系映射是 Hibernate 的 ORM 层的工作。

于 2012-08-17T11:48:42.483 回答