【问题标题】:Ibatis/MyBatis select dynamically without need to create any Pojo / MapperIbatis/MyBatis 动态选择,无需创建任何 Pojo/Mapper
【发布时间】:2011-07-05 11:38:03
【问题描述】:

有没有办法使用Ibatis/MyBatis动态选择/更新/删除?

当我说“动态”时,这意味着我根本不想创建任何 POJO/DataMapper。

欢迎提供任何 URL 示例。

【问题讨论】:

    标签: ibatis mybatis


    【解决方案1】:

    是的,只要将resultType属性设置为map,表格数据就会被放入列名到值的HashMap中。如果查询返回多于 1 行,则映射的行将被放入列表中。如果您想选择单个列,您可以只获取该值(如 String、int 等)或列表。

    <select id="test1" resultType="map">select * from user</select>
    <select id="test2" resultType="map" parameterType="int">
      select * from user where id=#{value}</select>
    <select id="test3" resultType="string">select name from user</select>
    ...
    // returns a list of maps
    List test = sqlSession.selectList("test1");
    
    // returns a single map
    Object map = sqlSession.selectOne("test2", 0);
    
    // returns a list of strings
    List names = sqlSession.selectList("test3");
    

    这适用于 MyBatis 3;我认为你可以在 iBatis 2 中做类似的事情。

    【讨论】:

    • 区别在于select *select name,假设名称是VARCHAR。它不一定是*,但只要您需要多于1 列,resultType 应该是map(或Pojo)。如果您只选择 1 列,请将 resultType 设置为任何列类型。
    【解决方案2】:

    以下方法可能很有用。 比如说,你有一些通用的选择界面,比如:

    public interface IAutoRepository {
        /**
         * The automatically generated insertPKs sql statements.
         * Parts of the query can be set manually in the sql (insert-select query).
         *
         * @param items     the {@link WhereStmt} statements
         * @return the inserted rows count
         */
        @Transactional
        <T extends WhereStmt> Integer insertPKs(@Param("items") List<T> items);
    
        /**
         * Returns the value based on the {@link Parameter} class
         *
         * @param param     the {@link Parameter} instance
         * @return the searched value in a {@link java.util.Map} form
         */
        @MapKey("VAL")
        <T extends Parameter> Map<String, Map<String, ?>> getDistinctValues(@Param("param") T param);
    }
    

    根据某些外部类型(例如,单列或日期范围或范围),您可以在模板Common.xml 中定义以下查询:

    <sql id="includeDistinctValues">
    SELECT
        <choose>
            <when test='param.type.name() == "set"'>
                DISTINCT ${param.column} AS val
            </when>
            <when test='param.type.name() == "date" or param.type.name() == "range"'>
                <some uid>              AS val,
                MIN(${param.minColumn}) AS min,
                MAX(${param.maxColumn}) AS max
            </when>
        </choose>
    FROM ${entityTable}
    </sql>
    

    你从mybatis收到的是java.util.Map。然后你可以像这样使用它:

    public enum StmtType {
        set((valMap) -> {
            final Set<String> distinctValues = valMap
                    .values()
                    .stream()
                    .map(val -> (String) val.get("VAL"))
                    //use in date/range case
                    //final Date minDate = (Date) val.get("MIN");
                    //final Date maxDate = (Date) val.get("MAX");
                    .collect(Collectors.toSet());
            return distinctValues;
        },
                (values, params) -> {
                    final SetParameter parameter = (SetParameter) params.getParams();
                    return new WhereSetStmt<>(parameter.getColumn(), values, params.getIncludeEmptyValues());
                });
        @Getter
        private Function<Map<String, Map<String, ?>>, ? extends Iterable> toValue;
        @Getter
        private BiFunction<Collection, DataParam, ? extends WhereStmt> toWhereStmt;
    
        StmtType(
                Function<Map<String, Map<String, ?>>, ? extends Iterable> toValue,
                BiFunction<Collection, DataParam, ? extends WhereStmt> toWhereStmt
        ) {
            this.toValue = toValue;
            this.toWhereStmt = toWhereStmt;
        }
    }
    

    其中SetParameter可以表示如下:

    @Getter
    public class SetParameter extends Parameter {
        /**
         * Column in sql query,
         */
        private final String column;
        public SetParameter(String column) {
            super(StmtType.set);
            this.column = column;
        }
    }
    

    此外,您可以定义一些 WhereStmt,例如:

    public abstract class WhereStmt {
        /**
         * Type of the statement
         */
        private final StmtType type;
        /**
         * Shall empty values be included.
         */
        private final boolean includeEmptyValues;
    }
    
    @Getter
    public class WhereSetStmt<T> extends WhereStmt {
        /**
         * The column for `column` IN (...) statement
         */
        private String column;
        /**
         * Values for `column` IN (...) statement
         */
        private Collection<T> values;
    
        public WhereSetStmt(String column, Collection<T> values, boolean includeEmptyValues) {
            super(StmtType.set, includeEmptyValues);
            this.column = column;
            this.values = values;
        }
    }
    
    @Getter
    @AllArgsConstructor
    public final class DataParam<P extends Parameter> {
        /**
         * Whether to include nullable values.
         */
        private final Boolean includeEmptyValues;
        /**
         * Represents database required information for later processing and sql statements generation.
         */
        private final P params;
    }
    

    最后,在 mybatis generic Common.xml 中你可以像这样使用它:

    <sql id="includeInsertPkSelect">
        SELECT DISTINCT(${id})
        FROM ${entityTable}
    </sql>
    
    <sql id="includeInsertPkWhere">
        <if test="items != null and items.size() > 0">
            AND
            <foreach collection="items" item="item" index="i" separator="AND">
                <choose>
                    <when test='item.type.name() == "set" and ( item.values != null and item.values.size() > 0 or item.includeEmptyValues )'>
                        (
                        <if test="item.values != null and item.values.size() > 0">
                            ${item.column} IN
                            <foreach item="value" collection="item.values" separator="," open="("
                                     close=")">
                                #{value}
                            </foreach>
                            <if test="item.includeEmptyValues">
                                OR
                            </if>
                        </if>
                        <if test="item.includeEmptyValues">
                            ${item.column} IS null
                        </if>
                        )
                    </when>
    
                    <when test='item.type.name() == "date"'>
                        (
                        COALESCE(${item.column}, SYSDATE + 1000000)
                        BETWEEN #{item.from} AND #{item.to}
                        <if test="item.includeEmptyValues">
                            OR ${item.column} IS NULL
                        </if>
                        )
                    </when>
                    <when test='item.type.name() == "range"'>
                        (
                        COALESCE(${item.column}, 1000000000)
                        BETWEEN #{item.from} AND #{item.to}
                        <if test="item.includeEmptyValues">
                            OR ${item.column} IS NULL
                        </if>
                        )
                    </when>
                </choose>
            </foreach>
        </if>
    </sql>
    

    并结合sql语句作为模板,比如:

    <insert id='insertPKs'>
        INSERT INTO requiredTable
        <include refid="Common.includeInsertPkSelect">
            <property name="id" value="entityId"/>
            <property name="entityTable" value="entityTable"/>
        </include>
        <include refid="Common.includeInsertPkWhere">
            <property name="id" value="entityId"/>
            <property name="entityTable" value="entityTable"/>
        </include>
    </insert>
    

    【讨论】:

      【解决方案3】:

      是的,应该可以通过 API 在运行时构建映射,并使用 Maps 而不是实体类。

      【讨论】:

        【解决方案4】:

        是的,您可以在不使用 POJO 的情况下进行选择/更新/删除。 在 myBatis 中,您可以在 Mapper XML 中编写查询,并设置将从 SQL 语句返回的 resultType 并将其存储在对象中。

        例如,

            <mapper namespace = "data">
              <select id = "fetch" resultType = "java.util.HashMap">
                select * from table_name
              </select>
            </mapper>
        

        您可以使用地图列表来存储这些结果。

          List<Map<String,Object>> results = session.selectList("data.fetch");
        

        【讨论】:

          猜你喜欢
          • 2011-12-02
          • 2021-06-09
          • 2019-10-15
          • 2011-10-30
          • 1970-01-01
          • 2011-08-28
          • 1970-01-01
          • 2018-01-11
          • 1970-01-01
          相关资源
          最近更新 更多