当前位置: 首页 > 知识库问答 >
问题:

只使用一个查询和列别名的嵌套1:N实体的SpringJdbc RowMapper

南门刚捷
2023-03-14

我有许多查询,有许多选择字段和一些嵌套实体。这是嵌套实体结构的简化版本:

public class OuterEntity{
    private String name1;
    private String name2;
    private MiddleEntity middle;
    //... get/set..
 }

public class MiddleEntity{
    private String surname1;
    private String surname2;
    private InnerEntity inner;
    //... get/set..
}

public class InnerEntity{
    private String nickname1;
    private String nickname2;
    //... get/set..
}

所有实体都有1:N关系,所以我可以编写单个长查询来获取所有数据。我希望避免多次查询来分别获得每个单个实体。

select 
    o.name1
    o.name2
    m.surname1
    m.surname2
    i.nickname1
    i.nickname2
from outertable o
join middletable m on m.id=o.middle
join innertable i on i.id=m.inner
select 
    o.name1 as name1
    o.name2 as name1
    m.surname1 as middle_surname1
    m.surname2 as middle_surname2
    i.nickname1 as middle_inner_nickname1
    i.nickname2 as middle_inner_nickname2
from outertable o
join middletable m on m.id=o.middle
join innertable i on i.id=m.inner

共有1个答案

林德惠
2023-03-14

我现在什么也没找到,我试图基于BeanPropertyRowMapper Soruce编写一个自定义BeanWrapper。

import java.beans.PropertyDescriptor;
import java.math.BigDecimal;
import java.sql.Date;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.NotWritablePropertyException;
import org.springframework.beans.PropertyAccessorFactory;
import org.springframework.dao.DataRetrievalFailureException;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.support.JdbcUtils;
/**
 * @author tobia.scapin
 * 
 * BeanRowMapper for nesting beans of 1:n entity this uses query aliases to build entity nesting.
 * Field names should be exactly the same of bean property, respect cases and do not use underscore for field names
 * "id" columnname/alias is used to check if a nested entity should be null.
 * 
 * example:
 *      select
 *          a.p1 as property1
 *          b.id as entityname_id    //<-- if this is values is null, the entity will be null
 *          b.p1 as entityname_property1
 *          b.p2 as entityname_property2
 *          c.id as entityname_subentity_id     //<-- if this is values is null, the subentity will be null
 *          c.p1 as entityname_subentity_property1
 *      from a,b,c
 *
 * @param <T>
 */
public class NestedBeanAliasRowMapper<T> implements RowMapper<T> {

    private static final String NESTING_SEPARATOR = "_";
    private static final String NULLIZER_FIELD = "id";

    @SuppressWarnings("rawtypes")
    private final static List<Class> TYPES;
    static{
        TYPES=Arrays.asList(new Class[]{ int.class, boolean.class, byte.class, short.class, long.class, double.class, float.class, Boolean.class, Integer.class, Byte.class, Short.class, Long.class, BigDecimal.class, Double.class, Float.class, String.class, Date.class});
    }

    private Class<T> mappedClass;
    private Map<String, PropertyDescriptor> mappedFields;
    private Map<String, PropertyDescriptor> mappedBeans;
    @SuppressWarnings("rawtypes")
    private Map<Class,NestedBeanAliasRowMapper> mappersCache=new HashMap<Class,NestedBeanAliasRowMapper>();
    private Map<String,BeanProp> beanproperties=null;

    public NestedBeanAliasRowMapper(Class<T> mappedClass) {
        initialize(mappedClass);
    }

    /**
     * Initialize the mapping metadata for the given class.
     * @param mappedClass the mapped class
     */
    protected void initialize(Class<T> mappedClass) {
        this.mappedClass = mappedClass;
        mappersCache.put(mappedClass, this);
        this.mappedFields = new HashMap<String, PropertyDescriptor>();
        this.mappedBeans = new HashMap<String, PropertyDescriptor>();
        PropertyDescriptor[] pds = BeanUtils.getPropertyDescriptors(mappedClass);
        for (PropertyDescriptor pd : pds) {
            if (pd.getWriteMethod() != null) {
                if(TYPES.contains(pd.getPropertyType()))                    
                    this.mappedFields.put(pd.getName(), pd);
                else
                    this.mappedBeans.put(pd.getName(), pd);
            }
        }
    }

    @Override
    public T mapRow(ResultSet rs, int rowNumber) throws SQLException {
        ResultSetMetaData rsmd = rs.getMetaData();
        int columnCount = rsmd.getColumnCount();
        List<Integer> cols=new ArrayList<Integer>();
        for (int index = 1; index <= columnCount; index++)
            cols.add(index);
        return mapRow(rs, rowNumber, cols, "", true);
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    public T mapRow(ResultSet rs, int rowNumber, List<Integer> cols, String aliasPrefix, boolean root) throws SQLException {
        T mappedObject = BeanUtils.instantiate(this.mappedClass);
        BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(mappedObject);
        ResultSetMetaData rsmd = rs.getMetaData();
        if(rowNumber==0) beanproperties=new HashMap<String,BeanProp>();
        for (int index : cols) {
            String column = JdbcUtils.lookupColumnName(rsmd, index);
            if(aliasPrefix!=null && column.length()>aliasPrefix.length() && column.substring(0, aliasPrefix.length()).equals(aliasPrefix))
                column=column.substring(aliasPrefix.length()); //remove the prefix from column-name
            PropertyDescriptor pd = this.mappedFields.get(column);
            if (pd != null) {
                try {
                    Object value = getColumnValue(rs, index, pd);
                    if(!root && NULLIZER_FIELD.equals(column) && value==null)
                        return null;
                    bw.setPropertyValue(pd.getName(), value);
                }
                catch (NotWritablePropertyException ex) {
                    throw new DataRetrievalFailureException("Unable to map column '" + column + "' to property '" + pd.getName() + "'", ex);
                }
            }else if(rowNumber==0 && column.contains(NESTING_SEPARATOR)){
                String[] arr=column.split(NESTING_SEPARATOR);
                column=arr[0];
                PropertyDescriptor bpd = this.mappedBeans.get(column);
                if(bpd!=null){
                    BeanProp beanprop=beanproperties.get(column);
                    if(beanprop==null){
                        beanprop=new BeanProp();
                        beanprop.setClazz(bpd.getPropertyType());
                        beanproperties.put(column, beanprop);
                    }
                    beanprop.addIndex(index);
                }
            }
        }
        if(!beanproperties.isEmpty()) for (String beanname : beanproperties.keySet()) {
            BeanProp beanprop=beanproperties.get(beanname);     
            NestedBeanAliasRowMapper mapper=mappersCache.get(beanprop.getClazz());
            if(mapper==null){
                mapper=new NestedBeanAliasRowMapper<>(beanprop.getClazz());
                mappersCache.put(beanprop.getClazz(), mapper);
            }
            Object value = mapper.mapRow(rs, rowNumber, beanprop.getIndexes(), aliasPrefix+beanname+NESTING_SEPARATOR, false);
            bw.setPropertyValue(beanname, value);           
        }
        return mappedObject;
    }

    protected Object getColumnValue(ResultSet rs, int index, PropertyDescriptor pd) throws SQLException {
        return JdbcUtils.getResultSetValue(rs, index, pd.getPropertyType());
    }

    public static <T> BeanPropertyRowMapper<T> newInstance(Class<T> mappedClass) {
        return new BeanPropertyRowMapper<T>(mappedClass);
    }

    @SuppressWarnings("rawtypes")
    private class BeanProp{
        private Class clazz;
        private List<Integer> indexes=new ArrayList<Integer>(); 

        public Class getClazz() {
            return clazz;
        }
        public void setClazz(Class clazz) {
            this.clazz = clazz;
        }
        public List<Integer> getIndexes() {
            return indexes;
        }
        public void addIndex(Integer index) {
            this.indexes.add(index);
        }


    }

}
 类似资料:
  • 问题内容: 默认情况下,我想给我的body元素添加绿色边框。在支持视网膜显示的设备上,我要首先检查尺寸。在ipad上,我想给我的身体一个红色边框,在iphone上,我想给它一个蓝色边框。但是像这样嵌套媒体查询是行不通的: 问题答案: 否。您需要使用运算符并将其写为两个查询。但是,您可以在将编译为CSS的SCSS中执行此操作,但是它将通过展开它们并使用运算符将它们组合在一起。 这是一个普遍的问题,一

  • 问题内容: 我想使用ES进行图书搜索。因此,我决定将作者姓名和标题(作为嵌套文档)放入索引,如下所示: 我不明白的是:如何构造搜索查询,以便在搜索“一二”时仅找到第二本书,而在搜索“二三”时什么也找不到,而在搜索“一”时所有图书呢? 问题答案: 也许是这样的? 该查询基本上说一个文件必须有and 。您可以轻松地重新配置该查询。例如,如果您只想搜索作者,请删除嵌套部分。如果您想要另一本书,请更改嵌套

  • 我有下一个关系(只是示例)用户0/1-*故事当我想获得包含用户的故事列表时,我会进行下一个查询。 我注意到ef向db发出了额外的请求以获取用户(但这不应该是因为我使用了include)。没有AsNoTrack(),一切都很好。 看起来这是一个可为null的关系问题,因为不允许为null的关系可以很好地工作。 有人有类似的问题吗?可能是“允许空”关系的预期行为。

  • 问题内容: 我正在使用Nest Elastic并使用Head插件为布尔搜索构建查询,我正在合并多个查询 有关数据库结构和弹性映射的注释 数据库中的每个文档都链接到特定的profileId,后者又具有多个属性 每个文档都有与其关联的多个属性值 在此查询中,我要获取具有特定配置文件和属性值> 30的所有文档,同时要记住,此属性应仅具有ID 2。 SQL查询: 从文档d内部联接attributeValu

  • jOOQ可以将查询结果映射到POJO中的多个嵌套列表吗?例如,我有一个POJO VM,它代表一个虚拟机。它具有'networks'属性,这是一个类型为network的列表。它还有一个'hdds'属性,这是一个类型为'hdd'的列表。该查询连接VM、HDD和Networks表。我是否可以“获取”到vm.class并期望jOOQ“做正确的事情”?

  • 我有一个名为Student实体,它有几个与Student相关的字段,还有一个具有一个任何关系的主题列表。在这里,我需要得到所有的字段,包括主题列表,除了“图像”,因为它使用更多的内存,它需要很长的时间来检索所有行。有没有人可以说,如何创建投影和标准,以检索这个学生对象,没有图像字段单独?有没有像“AliastObeAnneStedResultTransformer”这样的自定义结果转换器?因为在我