MyBatis ...在循环中获取最后插入的 ID "foreach"
MyBatis ... Get Last insert ID in loop "foreach"
谢谢你的帮助:)
我试图获取最后一个 ID,并阅读了很多关于它的文章 post,但我还没来得及在我的案例中应用它。
第一个Class
private Date date;
private List<AdsEntity> adsDetails;
... getters and setters
第二个Class(AdsEntity)
private int id;
private String description;
有我尝试获取最后一个 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);
在调试模式下,当我观看 List 时,我看到 id 仍然为 0 并且没有获得任何 id。
所以我写的没有锻炼:(
解决方案尝试了@Roman Konoval 的回答:
@Roman Konoval
我按照你说的做了, table 设置得很好 :)
还有一个问题,ID不符合
@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);
}
}
感谢您的帮助:)
解决方案添加到来自@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 = "adsDetail.id", resultType = int.class )
void saveAdsDetails(@Param("adsDetail") AdsDetailsEntity adsDetail);
default void saveManyAdsDetails(@Param("adsDetails") List<AdsDetailsEntity> adsDetails)
{
for(AdsDetailsEntity adsDetail:adsDetails) {
saveAdsDetails(adsDetail);
}
}
感谢大家的 3 条建议!!!
是的。没用。
foreach
-tag 没有 support/provide 以下属性 statement
、keyProperty
order
和 resultType
如果您需要每个插入项目的 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
提供属性 idMyInfo
和 adDate
编辑 2019-08-21 07:25
一些解释
提到提到的 dtd,<selectKey>
-tag 只允许作为 <insert>
和 <update>
的直接子代。它指的是传递给映射器方法并声明为 parameterType
.
的单个 Object
它只执行一次,它的 order
属性 告诉 myBatis 在 insert/update 语句之前或之后执行它。
在您的情况下,<script>
创建了一个语句,该语句被发送到数据库并由其处理。
允许@Insert
与<script>
组合,<foreach>
内部和@SelectKey
组合。但是 myBatis 没有 intercept/observe/watch 数据库处理给定的语句。如前所述,@SelectKey
在 @Insert
执行之前或之后仅执行一次。所以在你的特定情况下 @SelectKey
returns 最后插入的元素的 id。如果您的脚本插入十个元素,则只会返回第十个元素的新生成的 id。但是 @SelectKey
需要 class-属性 以及 getter 和 setter 来将选定的 ID 放入 - List<?>
没有提供。
例子
假设您想保存一个 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-mapper 可能看起来像这样。 @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();
}
}
}
Chris 在 foreach
中写的关于无法获取 ID 的内容是正确的。然而,有一种方法可以在映射器中实现 id 获取,而无需在外部进行。如果您使用 say spring 并且没有单独的 DAO 层并且您的 mybatis 映射器是存储库,这可能会有所帮助。
您可以使用 default interface method (see 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);
}
}
MyBatis 可以将生成的键分配给列表参数 if 你的 DB/driver 通过 java.sql.Statement#getGeneratedKeys()
支持多个生成的键(MS SQL 服务器,例如,does not support it,ATM)。
以下示例使用 MySQL 5.7.27 + Connector/J 8.0.17 进行测试(您应该在问题中包含版本信息)。
请务必使用最新版本的 MyBatis (=3.5.2),因为最近有一些规范更改和错误修复。
Table定义:
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/>
)。
谢谢你的帮助:)
我试图获取最后一个 ID,并阅读了很多关于它的文章 post,但我还没来得及在我的案例中应用它。
第一个Class
private Date date;
private List<AdsEntity> adsDetails;
... getters and setters
第二个Class(AdsEntity)
private int id;
private String description;
有我尝试获取最后一个 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);
在调试模式下,当我观看 List 时,我看到 id 仍然为 0 并且没有获得任何 id。
所以我写的没有锻炼:(
解决方案尝试了@Roman Konoval 的回答:
@Roman Konoval
我按照你说的做了, table 设置得很好 :) 还有一个问题,ID不符合
@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);
}
}
感谢您的帮助:)
解决方案添加到来自@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 = "adsDetail.id", resultType = int.class )
void saveAdsDetails(@Param("adsDetail") AdsDetailsEntity adsDetail);
default void saveManyAdsDetails(@Param("adsDetails") List<AdsDetailsEntity> adsDetails)
{
for(AdsDetailsEntity adsDetail:adsDetails) {
saveAdsDetails(adsDetail);
}
}
感谢大家的 3 条建议!!!
是的。没用。
foreach
-tag 没有 support/provide 以下属性 statement
、keyProperty
order
和 resultType
如果您需要每个插入项目的 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
提供属性 idMyInfo
和 adDate
编辑 2019-08-21 07:25
一些解释
提到提到的 dtd,<selectKey>
-tag 只允许作为 <insert>
和 <update>
的直接子代。它指的是传递给映射器方法并声明为 parameterType
.
Object
它只执行一次,它的 order
属性 告诉 myBatis 在 insert/update 语句之前或之后执行它。
在您的情况下,<script>
创建了一个语句,该语句被发送到数据库并由其处理。
允许@Insert
与<script>
组合,<foreach>
内部和@SelectKey
组合。但是 myBatis 没有 intercept/observe/watch 数据库处理给定的语句。如前所述,@SelectKey
在 @Insert
执行之前或之后仅执行一次。所以在你的特定情况下 @SelectKey
returns 最后插入的元素的 id。如果您的脚本插入十个元素,则只会返回第十个元素的新生成的 id。但是 @SelectKey
需要 class-属性 以及 getter 和 setter 来将选定的 ID 放入 - List<?>
没有提供。
例子
假设您想保存一个 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-mapper 可能看起来像这样。 @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();
}
}
}
Chris 在 foreach
中写的关于无法获取 ID 的内容是正确的。然而,有一种方法可以在映射器中实现 id 获取,而无需在外部进行。如果您使用 say spring 并且没有单独的 DAO 层并且您的 mybatis 映射器是存储库,这可能会有所帮助。
您可以使用 default interface method (see 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);
}
}
MyBatis 可以将生成的键分配给列表参数 if 你的 DB/driver 通过 java.sql.Statement#getGeneratedKeys()
支持多个生成的键(MS SQL 服务器,例如,does not support it,ATM)。
以下示例使用 MySQL 5.7.27 + Connector/J 8.0.17 进行测试(您应该在问题中包含版本信息)。
请务必使用最新版本的 MyBatis (=3.5.2),因为最近有一些规范更改和错误修复。
Table定义:
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/>
)。