MyBatis ...获取循环“ foreach”中的最后一个插入ID

时间:2019-08-20 17:26:05

标签: mybatis

谢谢您的帮助:)

我试图获取最后一个ID,并阅读了很多有关它的文章,但我没有来此应用。

头等舱

var data = [{monthName:"Jun",name:"web",number:1,month:6},{monthName:"Jul",name:"web",number:2,month:7},{monthName:"Aug",name:"web",number:2,month:8},{monthName:"Jun",name:"sales",number:12,month:6},{monthName:"Jul",name:"sales",number:2,month:7}];

function mergeName(arr) {
  let map = data.reduce((a,c) => (a[c.name] = ({...c, 
            number: a[c.name] && a[c.name].number
                    ? a[c.name].number.push(c.number) && a[c.name].number 
                    : [c.number]}),a),{});
  return Object.entries(map).map(o => ({name: o[0], number: o[1].number}))
}

console.log(mergeName(data))

第二类(AdsEntity)

private Date date;
private List<AdsEntity> adsDetails;

    ... getters and setters

有代码我尝试获取最后一个ID:

映射器

private int id;
private String description;

在调试模式下,当我观看列表时,我看到ID仍为0,并且没有任何ID。

所以我写的东西没有锻炼:(


解决方案尝试使用@Roman Konoval的答案:

@Roman Konoval

我照你说的做了,桌子摆放得很好:) 仍然只是一个问题,ID未填写

@Insert({
    "<script>",
    "INSERT INTO tb_ads_details  (idMyInfo, adDate)"
    + " VALUES"
        + " <foreach item='adsDetails' index='index' collection='adsDetails' separator=',' statement='SELECT LAST_INSERT_ID()' keyProperty='id' order='AFTER' resultType='java.lang.Integer'>"
            + " (#{adsDetails.description, jdbcType=INTEGER}) "
        + " </foreach>  ",  
    "</script>"})
void saveAdsDetails(@Param("adsDetails") List<AdsDetailsEntity> adsDetails);

感谢您的帮助:)


解决方案从@Chris建议添加到@Roman Konoval提案

@Chris和@Roman Konoval

    @Insert("INSERT INTO tb_ads_details SET `idMyInfo` = #{adsDetail.idMyInfo, jdbcType=INTEGER}, `adDate` = #{adsDetail.adDate, jdbcType=DATE}")
    @SelectKey(statement = "SELECT LAST_INSERT_ID()", before = false, keyColumn = "id", keyProperty = "id", resultType = Integer.class )
    void saveAdsDetails(@Param("adsDetail") AdsDetailsEntity adsDetail);


    default void saveManyAdsDetails(@Param("adsDetails") List<AdsDetailsEntity> adsDetails)
    {
        for(AdsDetailsEntity adsDetail:adsDetails) {
            saveAdsDetails(adsDetail);
        }
    }

enter image description here

感谢大家的3条建议!

3 个答案:

答案 0 :(得分:2)

是的。它不起作用。

请看一下mapper.dtd

foreach-标记不支持/提供以下属性statementkeyProperty orderresultType

如果您需要每个插入项的ID,请让DataAccessObject处理迭代并在MapperInterface中使用类似的内容

@Insert("INSERT INTO tb_ads_details (idMyInfo, adDate) (#{adsDetail.idMyInfo, jdbcType=INTEGER}, #{adsDetail.adDate, jdbcType=DATE})")
@SelectKey(before = false, keyColumn = "ID", keyProperty = "id", resultType = Integer.class, statement = { "SELECT LAST_INSERT_ID()" } )
void saveAdsDetails(@Param("adsDetail") AdsDetailsEntity adsDetail);

请确保AdsDetailsEntity-Class提供属性idMyInfoadDate

编辑2019-08-21 07:25

一些解释

引用所提到的dtd <selectKey>标签仅允许作为<insert><update>的直接子代。它是指传递到映射器方法并声明为Object的单个parameterType

它仅执行一次,并且其order属性告诉myBatis是否在插入/更新语句之前或之后执行它。

在您的情况下,<script>创建一个发送到数据库并由数据库处理的单个语句。

允许将@Insert<script><foreach>结合在@SelectKey内部。但是myBatis不会拦截/观察/监视处理给定语句的数据库。如前所述,@SelectKey仅在@Insert执行之前或之后执行一次。因此,在您的特定情况下,@SelectKey返回最后插入的元素的ID。如果您的脚本插入了十个元素,则仅返回新生成的第十个元素的ID。但是@SelectKey需要具有getter和setter的类属性,以将所选ID放入List<?>不提供的ID中。

示例

让我们说您要保存一个Advertisement及其AdvertisementDetail s

Advertisement具有ID,日期和详细信息

public class Advertisement {
    private List<AdvertisementDetail> adDetails;
    private Date date;
    private int id;

    public Advertisement() {
        super();
    }

    // getters and setters
}

AdvertisementDetail有其自己的ID,说明和它所属的Advertisement的ID

public class AdvertisementDetail {
    private String description;
    private int id;
    private int idAdvertisement;

    public AdvertisementDetail() {
        super();
    }

    // getters and setters
}

MyBatis映射器可能看起来像这样。 @Param未使用,因此可以直接访问属性。

@Mapper
public interface AdvertisementMapper {
    @Insert("INSERT INTO tb_ads (date) (#{date, jdbcType=DATE})")
    @SelectKey(
            before = false,
            keyColumn = "ID",
            keyProperty = "id",
            resultType = Integer.class,
            statement = { "SELECT LAST_INSERT_ID()" })
    void insertAdvertisement(
            Advertisement ad);

    @Insert("INSERT INTO tb_ads_details (idAdvertisement, description) (#{idAdvertisement, jdbcType=INTEGER}, #{description, jdbcType=VARCHAR})")
    @SelectKey(
            before = false,
            keyColumn = "ID",
            keyProperty = "id",
            resultType = Integer.class,
            statement = { "SELECT LAST_INSERT_ID()" })
    void insertAdvertisementDetail(
            AdvertisementDetail adDetail);
}

DataAccessObject(DAO)可能看起来像这样

@Component
public class DAOAdvertisement {
    @Autowired
    private SqlSessionFactory sqlSessionFactory;

    public DAOAdvertisement() {
        super();
    }

    public void save(
            final Advertisement advertisement) {
        try (SqlSession session = this.sqlSessionFactory.openSession(false)) {
            final AdvertisementMapper mapper = session.getMapper(AdvertisementMapper.class);
            // insert the advertisement (if you have to)
            // its new generated id is received via @SelectKey
            mapper.insertAdvertisement(advertisement);
            for (final AdvertisementDetail adDetail : advertisement.getAdDetails()) {
                // set new generated advertisement-id
                adDetail.setIdAdvertisement(advertisement.getId());
                // insert adDetail
                // its new generated id is received via @SelectKey
                mapper.insertAdvertisementDetail(adDetail);
            }
            session.commit();
        } catch (final PersistenceException e) {
            e.printStackTrace();
        }
    }
}

答案 1 :(得分:1)

Chris关于无法在foreach中获取ID的内容是正确的。但是,有一种方法可以在映射器中实现id提取,而无需在外部进行。如果您使用say spring并没有单独的DAO层,而mybatis映射器是存储库,则这可能会有所帮助。

您可以使用default interface method(请参阅another tutorial)通过为单个项目插入调用映射器方法来插入项目列表,而单个项目插入方法本身会进行id选择:

interface ItemMapper {
  @Insert({"insert into myitem (item_column1, item_column2, ...)"})
  @SelectKey(before = false, keyColumn = "ID", 
     keyProperty = "id", resultType = Integer.class, 
     statement = { "SELECT LAST_INSERT_ID()" } )
  void saveItem(@Param("item") Item item);

  default void saveItems(@Param("items") List<Item> items) {
    for(Item item:items) {
      saveItem(item);
  }
}

答案 2 :(得分:1)

如果您的数据库驱动程序通过java.sql.Statement#getGeneratedKeys()(MS SQL Server,例如,does not support it,ATM),数据库/驱动程序支持多个生成的密钥,则MyBatis可以将生成的密钥分配给列表参数。 。

以下示例已在MySQL 5.7.27 + Connector / J 8.0.17中进行了测试(您应该在问题中包含版本信息)。
请确保使用最新版本的MyBatis(= 3.5.2),因为最近进行了一些规格更改和错误修复。

表定义:

CREATE TABLE le tb_ads_details (
  id INT PRIMARY KEY AUTO_INCREMENT,
  description VARCHAR(32)
)

POJO:

private class AdsDetailsEntity {
  private int id;
  private String description;
  // getters/setters
}

映射器方法:

@Insert({
  "<script>",
  "INSERT INTO tb_ads_details (description) VALUES",
  "<foreach item='detail' collection='adsDetails' separator=','>",
  "  (#{detail.description})",
  "</foreach>",
  "</script>"
})
@Options(useGeneratedKeys = true, keyProperty="adsDetails.id", keyColumn="id")
void saveAdsDetails(@Param("adsDetails") List<AdsDetailsEntity> adsDetails);

注意:插入大量行时,应使用批处理插入(带有ExecutorType.BATCH)而不是多行插入(= {<foreach/>)。