Ibatis/MyBatis动态选择,无需创建任何Pojo/Mapper

发布于 2024-11-18 15:46:41 字数 112 浏览 4 评论 0原文

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

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

任何 URL 示例都会受到欢迎。

Is there any way to select/update/delete dynamically using Ibatis/MyBatis?

When I say "dynamically" it means I don't want to create any POJO/DataMapper at all.

Any URL example would be welcomed.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

少年亿悲伤 2024-11-25 15:46:41

是的,只需将 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 中做类似的事情。

Yes, just set the resultType attribute to map and the table data will be placed into a HashMap of column names to values. If the query returns more than 1 row, the mapped rows will be put into a List. If you want to select a single column, you can get just that value (as String, int, etc) or as a list.

<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");

This applies to MyBatis 3; I think you can do something similar in iBatis 2.

一直在等你来 2024-11-25 15:46:41

以下方法可能很有用。
假设您有一些通用选择界面,例如:

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>

The following approach can be useful.
Say, you have some generic select interface, like:

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);
}

According to some external type (say, single column or date range or range whatsoever) you can define the following query in the template 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>

What you receive from mybatis is the java.util.Map. Then you can you use it some kind like:

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;
    }
}

where the SetParameter can be represented as the following:

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

Moreover, you can define some WhereStmt like:

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;
}

Finally, in mybatis generic Common.xml you can use it like:

<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>

And combine sql statements as the template, say:

<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>
痴情 2024-11-25 15:46:41

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

Yes, it should be possible to build the mapping in runtime through an API, and to use Maps instead of entity classes.

晨敛清荷 2024-11-25 15:46:41

是的,您可以在不使用 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");

Yes, you can do select/update/delete without using POJO.
In myBatis you can write your query in Mapper XML and set the resultType that will be returned from SQL statement and store that in objects.

For Example,

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

You can use List of Maps to store those results.

  List<Map<String,Object>> results = session.selectList("data.fetch");
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文