CLOVER๐Ÿ€

That was when it all began.

ใƒ–ใƒญใ‚ฐใ‚’ๆ›ธใๅง‹ใ‚ใฆใ‹ใ‚‰ใ€10ๅนด็ตŒใกใพใ—ใŸ

ใชใ‚“ใจ

ใ“ใฎใƒ–ใƒญใ‚ฐใงใ™ใŒใ€ๆ›ธใๅง‹ใ‚ใฆใ‹ใ‚‰10ๅนด็ตŒใฃใฆใ„ใŸใ‚ˆใ†ใงใ™ใ€‚

ๆœ€ๅˆใซๆ›ธใ„ใŸใฎใฏ2011ๅนด5ๆœˆ14ๆ—ฅใงใ€ใ€ŒไปŠๆ—ฅใ‹ใ‚‰้–‹่จญใ€‚ใ€ใจใพใฃใŸใใ‚„ใ‚‹ๆฐ—ใŒใชใ•ใใ†ใช1ๆœฌ็›ฎใ‹ใ‚‰ๅง‹ใพใฃใฆใ„ใ‚‹ใฎใงใ™ใŒใ€‚

็ถšใ‘ใฆ10ๅนด็ตŒใฃใŸใจใ„ใ†ใ“ใจใซใ€ไปŠๅนดใซใชใฃใฆใ€Œใฉใ‚Œใใ‚‰ใ„็ถšใ‘ใฆใ‚‹ใฎ๏ผŸใ€ใจ่žใ‹ใ‚Œใฆๆฐ—ใฅใ„ใฆใ—ใพใ„ใพใ—ใŸใ€‚

ใŸใถใ‚“ใ€่žใ‹ใ‚Œใฆใชใ‹ใฃใŸใ‚‰ๆฐ—ใฅใ„ใฆใชใ„ใงใ™ใญใ€‚

ใ“ใฎใƒ–ใƒญใ‚ฐใงใฏใ€ๅฎŸ้š›ใฎ่‡ชๅˆ†ใฎๅ‹‰ๅผทใฎ่จ˜้Œฒไปฅๅค–ใปใจใ‚“ใฉๆ›ธใ„ใฆใใฆใ„ใชใ„ใฎใงใ™ใŒใ€ใ“ใ†ใ„ใ†ๅŒบๅˆ‡ใ‚Šใฃใฝใ„ๅนดใใ‚‰ใ„
ใŸใพใซใฏใ„ใ„ใ‹ใชใใจใ„ใ†ใ“ใจใงใ€‚

ๆŒฏใ‚Š่ฟ”ใ‚Šใ‚’ๅ…ผใญใฆใ€ใคใ‚‰ใคใ‚‰ใจๆ›ธใ„ใฆใฟใพใ—ใ‚‡ใ†ใ€‚่‡ชๅˆ†ใŒใ€Œใฉใ†ใ—ใฆใ ใฃใ‘๏ผŸใ€ใจใ„ใ†ใฎใ‚’่€ƒใˆใชใŒใ‚‰ๆ›ธใ„ใฆใ„ใพใ™ใ€‚

ใ‚‚ใ†ๆ•ฐๅนด็ตŒใฃใŸๆ™‚ใซใ€่‡ชๅˆ†ใง่ฆ‹่ฟ”ใ—ใŸใ‚‰ใฉใ†ๆ€ใ†ใงใ—ใ‚‡ใ†ใญ๏ผŸ

ใƒ–ใƒญใ‚ฐใ‚’ๅง‹ใ‚ใŸ็†็”ฑ

่‡ชๅˆ†ใฎๅ‹‰ๅผทใฎใŸใ‚ใซๅง‹ใ‚ใพใ—ใŸใ€‚

ๅฝ“ๆ™‚ใ€ๆœฌใ‚’่ชญใ‚“ใ ใ‚Šใ€ๆœฌใ‚„Webใ‚’่ฆ‹ใชใŒใ‚‰ใ‚ณใƒผใƒ‰ใ‚’ๆ›ธใ„ใฆใ„ใฆใ‚‚ใ€ใฉใ†ใ‚‚่บซใซใคใ‹ใชใ„ใ“ใจใซๆ‚ฉใ‚“ใงใ„ใฆใ€‚
ๆ–‡็ซ ใงๆ›ธใ„ใŸๆ–นใŒ่ฆšใˆใ‚‹ใ ใ‚ใ†ใ€ใจใ„ใ†ๅŠนๆžœใ‚’็‹™ใฃใฆๅง‹ใ‚ใŸใฎใŒใใฃใ‹ใ‘ใงใ™ใ€‚

ๆƒณๅฎšๅฏพ่ฑก่ชญ่€…

ๅฏพ่ฑก่ชญ่€…ใฏใ€่‡ชๅˆ†่‡ช่บซใงใ™ใ€‚

ใ”ใ็จ€ใซใ€่ชญใฟๆ‰‹ใฎๅญ˜ๅœจใ‚’ๆ„่ญ˜ใ—ใฆๆ›ธใ่จ˜ไบ‹ใ‚‚ใ‚ใ‚Šใพใ™ใŒ๏ผˆๆœ€่ฟ‘ใ‚„ใฃใฆใชใ„ใงใ™ใŒใ€Advent Calendarใชใฉ๏ผ‰ใ€ๅŸบๆœฌ็š„ใซใฏ
่‡ชๅˆ†ใฎๅ‹‰ๅผทใฎใŸใ‚ใ€ๆœชๆฅใฎ่‡ชๅˆ†ใŒ่ฆ‹ใฆ่ชญใฟ่ฟ”ใ—ใฆใ€ๆ€ใ„ๅ‡บใ›ใ‚‹ใŸใ‚ใซๆ›ธใ„ใฆใ„ใพใ™ใ€‚

1ๅ›žใกใ‚ƒใ‚“ใจๆ›ธใ„ใฆใŠใ‘ใฐใ€ใ‚ใจใง่ฆ‹ใฆใ‚‚ๆ›ธใ„ใŸๅฝ“ๆ™‚ใซใฉใ†ใ„ใ†ใ“ใจใ‚’ใ‚„ใฃใŸใฎใ‹ใ€ๆ„ๅค–ใจๆ€ใ„ๅ‡บใ›ใ‚‹ใ‚‚ใฎใ ใฃใŸใ‚Šใ—ใพใ™ใ€‚

ใจใฏใ„ใˆใ€ใŸใพใซๆ›ธใ„ใŸใ“ใจ่‡ชไฝ“ใ‚’ๅฟ˜ใ‚Œใฆใ„ใฆใ€ๅŒใ˜ใ‚ˆใ†ใช่จ˜ไบ‹ใ‚’ๆ›ธใ„ใฆใ—ใพใ†ใ“ใจใ‚‚ใ‚ใ‚Šใพใ—ใŸใ‘ใฉใญใ€‚

ใ‘ใฃใ“ใ†ๅ†—้•ทๆ„Ÿใฎใ‚ใ‚‹ใ‚ฟใ‚คใƒˆใƒซใฎใ‚‚ใฎใŒๅคšใ„ใจๆ€ใ†ใฎใงใ™ใŒใ€ใ“ใ‚Œใฏใ‚ญใƒผใƒฏใƒผใƒ‰ใง่‡ชๅˆ†ใŒๆคœ็ดขใ—ใ‚„ใ™ใใ™ใ‚‹ใŸใ‚ใงใ™ใ€‚

่‡ชๅˆ†ไปฅๅค–ใฎไบบใŒ่ชญใ‚€ใ“ใจใฏใปใจใ‚“ใฉๆฐ—ใซใ—ใฆใ„ใชใใฆใ€ใ€Œใ“ใ‚“ใชใƒ–ใƒญใ‚ฐใฎ่จ˜ไบ‹ใงใ‚‚ใ€่ชญใ‚ใ‚‹ใ‚ˆใ†ใชใ‚‰ใ€ใชใซใ‹ๅฝนใซ็ซ‹ใคใ‚ˆใ†ใง
ใ‚ใ‚Œใฐใฉใ†ใžใ€ใจใ„ใฃใŸ ใ‚นใ‚ฟใƒณใ‚นใงใ‚„ใฃใฆใ„ใพใ™ใ€‚ใ“ใ‚Œใฏใ€ใ“ใฎใƒ–ใƒญใ‚ฐใ‚’ๅง‹ใ‚ใŸๆ™‚ใ‹ใ‚‰ใšใฃใจใใ†ๆ€ใฃใฆใ„ใพใ™ใ€‚

ใชใฎใงใ€ใ€Œ็ต่ซ–ใ‚’ใ‚ใ‹ใ‚Šใ‚„ใ™ใใพใจใ‚ใ‚‹ใ€ใจใ„ใฃใŸใ“ใจใฏใปใจใ‚“ใฉใ—ใฆใ„ใชใใฆใ€ใ€Œใ“ใ‚Œใ‚’ใ‚„ใ‚ใ†ใ€ใ€ใ€Œใใ—ใฆใ“ใ†ใ ใฃใŸใ€
ใจใ„ใ†ใ“ใจใ‚’ๆทกใ€…ใจๆ›ธใ„ใฆใ„ใพใ™ใ€‚

ๅฝ“ๅˆใ‹ใ‚‰ใ€ใ€Œใ“ใ‚“ใชใƒ–ใƒญใ‚ฐใ‚’่ฆ‹ใ‚‹ๅฅ‡็‰นใชไบบใชใ‚“ใฆใ„ใชใ„ใ ใ‚ใ†ใ€ใจๆ€ใฃใฆๆ›ธใ„ใฆใ„ใ‚‹ใ‚“ใงใ™ใ‘ใฉใญใ€‚
ใใ‚Œใฏใ€่จ˜ไบ‹ใธใฎใ‚ขใ‚ฏใ‚ปใ‚นๆ•ฐใ‹ใ‚‰ใ‚‚่จ€ใˆใ‚‹ใจๆ€ใ†ใฎใงใ™ใŒใ€‚

ใใ†ใ„ใˆใฐใ€ใฏใฆใชใƒ–ใƒญใ‚ฐใซ็งป่กŒใ—ใฆใ‹ใ‚‰ใ€่ชญ่€…ใฎๅข—ใˆๆ–นใŒไธŠใŒใฃใŸๆฐ—ใŒใ—ใพใ™ใ€‚ใฉใ†ใ—ใฆใชใ‚“ใงใ—ใ‚‡ใ†๏ผŸ

ใชใ‚“ใง"CLOVER"๏ผŸ

"CLOVER"่‡ชไฝ“ใซๆ„ๅ‘ณใฏ่พผใ‚ใฆใ„ใพใ›ใ‚“ใ€‚

ใ“ใฎใƒ–ใƒญใ‚ฐใฎๅๅ‰ใฏใ‚ใ‚‹้›†ๅˆใ‹ใ‚‰้Ÿณใฎ้Ÿฟใใง้ธใ‚“ใ ใ‚‚ใฎใงใ€ๆ™‚ใ€…ๅค‰ใˆใฆใ„ใŸใฎใงใ™ใŒใ€"CLOVER"ใซๅค‰ใˆใŸๅพŒใซๆ„ๅค–ใจ
่ฆ‹ใ‚‰ใ‚Œใฆใ„ใ‚‹ใ“ใจใŒใ‚ใ‹ใฃใฆใ—ใพใ„ใ€ๅค‰ใˆใ‚‹ใซๅค‰ใˆใ‚‰ใ‚ŒใชใใชใฃใฆไปŠใซ่‡ณใ‚Šใพใ™ใ€‚

็พๆ™‚็‚นใงใ€ๆ›ธใ„ใŸ่จ˜ไบ‹ๆ•ฐ

ๅ˜็ด”ใชๆ•ฐใชใ‚‰ใ€10ๅนดใง1,432ใ€‚

ใปใ‚“ใฎๆ•ฐ่กŒใ€ใฟใŸใ„ใชใ‚‚ใฎใ‚‚ใ‚ใ‚Šใพใ™ใ‘ใฉใ€‚

ไป–ใจๆฏ”่ผƒใ™ใ‚‹ใ“ใจใ‚‚ใ€ใ—ใŸใ„ใจใ‚‚ๆ€ใฃใŸใ“ใจใŒใชใ„ใฎใงใ€ใ“ใ‚ŒใŒๅคšใ„ใ€ๅฐ‘ใชใ„ใฏใ‚ˆใใ‚ใ‹ใ‚Šใพใ›ใ‚“ใ€‚

็ถšใ‘ใฆใ„ใ‚‹็†็”ฑ

่‡ชๅˆ†ใฎๅ‹‰ๅผทใฎใŸใ‚ใซๆ›ธใ„ใฆใ„ใ‚‹ใฎใงใ€ๅฎŒๅ…จใซ่ถฃๅ‘ณใงใ™ใ€‚

ใ‚„ใ‚‹ใ“ใจใŒใชใ„ๆ™‚ใซใ€ใ€Œใใ†ใ„ใˆใฐใ€ใ“ใ‚Œ่ฉฆใ—ใฆใฟใŸใ„ใชใ€ใใ‚‰ใ„ใฎๅ‹•ๆฉŸใงๆ›ธใ„ใฆใ„ใ‚‹ใ“ใจใŒๅคšใ„ใงใ™ใ€‚

ไป–ใฎไบˆๅฎšใŒใ‚ใฃใŸใ‚‰ใ€ใตใคใ†ใซใใกใ‚‰ใ‚’ๅ„ชๅ…ˆใ—ใฆใพใ™ใ€‚

ใƒŽใƒซใƒžใจใ‹

ๆœˆใซ1ๅ›žๆŠ•็จฟใ™ใ‚‹ใ“ใจใ€‚ใ“ใ‚Œใ ใ‘ใงใ™ใ€‚

ๆ›ธใใƒใ‚ฟใฎๆฑบใ‚ๆ–น

ใ“ใฎใ‚ใŸใ‚Šใ‹ใ‚‰ใ€ใ€Œๅ‹‰ๅผทใ—ใŸใ„ใƒชใ‚นใƒˆใ€ใ‚’ไฝœใฃใฆใ€ใ‚ใ‚‹็จ‹ๅบฆๅ„ชๅ…ˆ้ †ไฝใ‚’ใคใ‘ใฆๆฑบใ‚ใฆใ„ใพใ™ใ€‚

  • ๅนดๆœซใฎๆŒฏใ‚Š่ฟ”ใ‚Šใงๅนดๅ†…ใซใ‚„ใฃใŸใ“ใจใจใ€ๆฅๅนดใซๅ‹‰ๅผทใ—ใฆใฟใŸใ„ใ“ใจใ‚’ใƒชใ‚นใƒˆใ‚ขใƒƒใƒ—ใ—ใฆใฟใŸใ‚‚ใฎ
  • ๆ—ฅๅธธใฎ็›ฎใซๅ…ฅใฃใŸๆƒ…ๅ ฑใ‹ใ‚‰ใ€่ˆˆๅ‘ณใŒใ‚ใฃใŸใ‚‚ใฎ
  • ไป•ไบ‹ใง่งฆใ‚Œใ‚‹ใ‚‚ใฎใงใ€่‡ชๅˆ†ใฎ่ˆˆๅ‘ณใจๅˆ่‡ดใ—ใŸใ‚‚ใฎ

ใใฎใƒชใ‚นใƒˆใซๆฒฟใฃใฆๅฟ…ใšใ‚„ใฃใฆใ„ใ‚‹ใ‹ใจใ„ใ†ใจใใ†ใงใ‚‚ใชใใ€ๆฐ—ใฅใใจใ€Œใ‘ใฃใ“ใ†้•ใ†ใ“ใจใ‚„ใฃใฆใ„ใ‚‹ใชใ€ใจใ„ใ†ใ“ใจใ‚‚
ๅคšใ€…ใ‚ใ‚Šใพใ™ใ€‚

ใ“ใ“ๆ•ฐๅนดใฏไป•ไบ‹ใงๆ‰ฑใ†ใ‚‚ใฎใซใคใ„ใฆใ‚‚ๆ›ธใๅ‰ฒๅˆใŒๅข—ใˆใฆใ„ใ‚‹ๆฐ—ใŒใ—ใพใ™ใŒใ€ไป•ไบ‹ใงไฝฟใ†ใ‚‚ใฎใงใ‚ใฃใฆใ‚‚
ใใ‚‚ใใ‚‚่‡ชๅˆ†ใฎ่ˆˆๅ‘ณใฎใชใ„ใ‚‚ใฎใงใชใ„ใจใ€ใ“ใฎใƒ–ใƒญใ‚ฐใซใฏ็™ปๅ ดใ—ใพใ›ใ‚“ใ€‚

ใ‚ใจใ€ใ“ใฎใƒ–ใƒญใ‚ฐใซๆ›ธใ„ใฆใ„ใ‚‹ๅ†…ๅฎนใฏใ€ใปใจใ‚“ใฉใŒใ„ใ‚ใ‚†ใ‚‹ใ€Œใ‚„ใฃใฆใฟใŸใ€ใจใ„ใ†ๅ†…ๅฎนใชใฎใงใ€ใ‚ญใƒฃใƒƒใƒใƒผใ•ใŒ
ใปใผใ‚ใ‚Šใพใ›ใ‚“ใ€‚่ˆˆๅ‘ณใฎใ‚ใ‚‹ๅ†…ๅฎนใ‚’่ฉฆใ—ใฆใ„ใฃใฆใ„ใ‚‹ๅฑฅๆญดใชใฎใงใ€ใพใ‚ใ€ใใ†ใชใ‚Šใพใ™ใ‚ˆใญใ€ใจใ€‚
่จ˜ไบ‹ใŒๆณจ็›ฎใ•ใ‚Œใชใ„ไธ€ๅ› ใงใ‚‚ใ‚ใ‚‹ๆฐ—ใฏใ—ใพใ™ใŒใ€‚

ใ“ใฎๅ‚พๅ‘ใŒๅค‰ใ‚ใ‚‹ใ“ใจใฏใ€ใชใ„ใจๆ€ใ„ใพใ™ใ€‚ไปŠใฎใจใ“ใ‚ใ€ใใ†ใ„ใ†ๆ„Ÿใ˜ใซใชใ‚‹ใ“ใจใฏใชใ„ใ‹ใชใใจใ€‚

ใ‚„ใ‚ŠใŸใ„ใƒใ‚ฟใฏใ€ๅๅˆ†ใซๆ‰ฑใˆใฆใ‚‹๏ผŸ

้Šใ‚“ใงใฟใŸใ„ใ“ใจใ€่ฉฆใ—ใฆใฟใŸใ„ใ“ใจใซ่ฟฝใ„ใคใ‹ใชใ„ใ“ใจใŒๅข—ใˆใพใ—ใŸใญใ€‚

ไป–ใฎใ‚‚ใฎใ‚’ๆ‰ฑใฃใฆใ„ใŸใ‚Šใ€ๅ„ชๅ…ˆๅบฆใ‚’ๅ…ฅใ‚Œๆ›ฟใˆใŸใ‚Šใ—ใŸ็ตๆžœใ€ๆ™‚้–“ใŒ้ŽใŽใฆใ—ใพใฃใฆใใฎใพใพ่ฆ‹้€ใฃใฆใ—ใพใ†ใ‚‚ใฎใŒ
ใ‘ใฃใ“ใ†ใ‚ใ‚Šใพใ™ใ€‚

ๅ…ฌๅผใฎGetting Startedใจใ‹ใ€ไป–ใฎไบบใŒๅŒใ˜ใ‚ˆใ†ใชๆƒ…ๅ ฑใ‚’ๆ›ธใ„ใฆใ‚‹ใ“ใจใ‚‚ๆ›ธใ„ใฆใ„ใ‚‹ใ‚ˆใ†ใช๏ผŸๆ„ๅ‘ณใชใ„ใฎใงใฏ๏ผŸ

ใตใคใ†ใซๆ›ธใ„ใฆใ„ใพใ™ใ€‚

ไป–ใฎๆ–นใŒๆ›ธใ„ใฆใ„ใŸใจใ—ใฆใ‚‚ใ€่‡ชๅˆ†ใง็ขบ่ชใ—ใฆใ€่‡ชๅˆ†ใฎ่จ€่‘‰ใงใ“ใฎใƒ–ใƒญใ‚ฐใงๆ›ธใ‘ใ‚‹ใใ‚‰ใ„ใซ็†่งฃใ—ใฆใฟใ‚‹ใ€็ดๅพ—ใ—ใฆใฟใ‚‹ใ€ใŒ
ใ“ใฎใƒ–ใƒญใ‚ฐใ‚’ๆ›ธใ„ใฆใ„ใ‚‹็†็”ฑใซใ‚‚ใชใฃใฆใ„ใพใ™ใ—ใ€‚

ใใ‚Œใซใ€ไป–ใฎๆ–นใฎ็†่งฃใ—ใŸใ“ใจใจใ€่‡ชๅˆ†ใŒ็ขบ่ชใ—ใฆ็†่งฃใ—ใŸใ“ใจใฏๅŒใ˜ใงใฏใชใ„ใจๆ€ใ„ใพใ™ใ—ใ€ใฉใ“ใ‹้•ใ„ใŒใ‚ใ‚‹ใงใ—ใ‚‡ใ†ใ€‚

ใ‚‚ใ—ใ‹ใ—ใŸใ‚‰ใพใฃใŸใๅŒใ˜ใ‹ใ‚‚ใ—ใ‚Œใพใ›ใ‚“ใŒใ€ใใ‚Œใ‚’็„กๆ„ๅ‘ณใ ใจใฏๆ€ใ„ใพใ›ใ‚“ใ€‚

ไป–ใฎๆ–นใฎ่จ˜ไบ‹ใ‚’ๆ›ธใๅ†™ใ—ใฆใ„ใ‚‹ใ‚ˆใ†ใชใ‚‰ๆ„ๅ‘ณใฏใชใ„ใจๆ€ใ„ใพใ™ใŒใ€ใ‚ขใ‚ฏใ‚ปใ‚นๆ•ฐใ‚’็จผใŽใŸใ„ใ‚ใ‘ใงใ‚‚ใชใ„ใงใ™ใ—ใ€‚

ใ‚ขใ‚ฏใ‚ปใ‚นใฎๅ‚พๅ‘ใจใ‹ใ€ใ‚ˆใ่ชญใพใ‚Œใฆใ„ใ‚‹่จ˜ไบ‹ใจใ‹๏ผŸ

ๅนณๆ—ฅใซใ‚ขใ‚ฏใ‚ปใ‚นใŒๅคšใใ€ไผ‘ใฟใฎๆ—ฅใฏใ‹ใชใ‚Šไธ‹ใŒใ‚Šใพใ™ใ€‚

Linuxใซ้–ขใ™ใ‚‹ใ‚‚ใฎใŒๅคšใ่ฆ‹ใ‚‰ใ‚Œใฆใ„ใ‚‹ๅ‚พๅ‘ใซใ‚ใ‚Šใพใ™ใŒใ€ใ™ใ”ใ็ชๅ‡บใ—ใŸใ‚‚ใฎใฏใ‚ใ‚Šใพใ›ใ‚“ใ—๏ผˆใ‚ขใ‚ฏใ‚ปใ‚น่งฃๆžใงใ‚‚1ไฝใŒ3๏ผ…ใจใ‹๏ผ‰ใ€
ใฏใฆใƒ–ใ‚‚ใ‚ใพใ‚Šใคใใพใ›ใ‚“ใ€‚

ใชใฎใงใ€ใชใซใŒใ‚ˆใ่ชญใพใ‚Œใฆใ„ใ‚‹ใจใ‹่‡ชๅˆ†ใงใ‚‚ใ‚ˆใใ‚ใ‹ใ‚Šใพใ›ใ‚“ใ€‚ใ„ใ‚ใ‚“ใชใƒšใƒผใ‚ธใซๆ•ฃใ‚‰ใฐใฃใฆใ„ใ‚‹ใ‚“ใ ใ‚ใ†ใชใ€ใจใฏ
ๆ€ใฃใฆใ„ใพใ™ใ€‚

ใชใŠใ€ใฏใฆใชใƒ–ใƒญใ‚ฐใซ็งป่กŒใ™ใ‚‹ใพใงใ‚ขใ‚ฏใ‚ปใ‚น่งฃๆžใ‚’่ฆ‹ใŸใ“ใจใŒใชใ‹ใฃใŸใฎใงใ€ใใ“ใงๅง‹ใ‚ใฆใ‚ขใ‚ฏใ‚ปใ‚นๅ‚พๅ‘ใ‚’็Ÿฅใ‚Šใพใ—ใŸใ—ใ€
ใฏใฆใชใƒ€ใ‚คใ‚ขใƒชใƒผใฎ้ ƒใซใฉใ†ใ ใฃใŸใฎใ‹ใฏใพใฃใŸใใ‚ใ‹ใ‚Šใพใ›ใ‚“ใ€‚

10ๅนดๆ›ธใ„ใฆใ„ใฆใ€ๆ›ธใๆ–นใŒๅค‰ใ‚ใฃใŸใจใ‹ใ‚ใ‚‹๏ผŸ

ๆ›ธใๅง‹ใ‚ใฎใ“ใ‚ใ‹ใ‚‰ใ™ใ‚‹ใจใ€่จ˜ไบ‹ใฎๆง‹ๆˆใŒใ ใ„ใถๅค‰ใ‚ใ‚Šใพใ—ใŸใ€‚

ๅคงใใๅค‰ใ‚ใฃใŸใฎใฏใ€็ขบ่ชใ—ใŸๆ™‚ใฎๆกไปถใ€็’ฐๅขƒใจใ€ๆƒ…ๅ ฑๆบใฎๆฎ‹ใ—ๆ–นใงใ™ใญใ€‚

่‡ชๅˆ†ใŒๆ›ธใๅง‹ใ‚ใŸ้ ƒใจๆฏ”ในใ‚‹ใจใ€ๆƒ…ๅ ฑใฎ้™ณ่…ๅŒ–ใ™ใ‚‹้€ŸๅบฆใŒๆ—ฉใพใฃใŸใ“ใจใ‚‚ใ‚ใฃใฆใ€ใ“ใ“ๆ•ฐๅนดใฏ

  • ใƒฉใ‚คใƒ–ใƒฉใƒชใ‚„ใƒŸใƒ‰ใƒซใ‚ฆใ‚งใ‚ขใ‚’ไฝฟใ†ๅ ดๅˆใฏใ€ใฉใฎใƒใƒผใ‚ธใƒงใƒณใซใคใ„ใฆใฎ่ฉฑใชใฎใ‹ใ‚’ๆ˜Ž่จ˜ใ™ใ‚‹
  • ๅ‰ๆใจใชใ‚‹็’ฐๅขƒใฎๆƒ…ๅ ฑใ‚’ๆ˜Ž่จ˜ใ™ใ‚‹

ใจใ„ใ†ใ“ใจใ‚’ใ€ๆ›ธใใ™ใŽใชใ„ใ€ใ‚ธใƒฃใƒžใซใชใ‚‰ใชใ„็จ‹ๅบฆใซๆ›ธใใ“ใจใซใ—ใฆใ„ใพใ™ใ€‚

ๅ‚็…งใ™ใ‚‹ๆƒ…ๅ ฑใฎๅผ•็”จๅ…ƒใซใคใ„ใฆใฏใ€

ใซใ™ใ‚‹ใ‚ˆใ†ใซๅผทใๆ„่ญ˜ใ—ใฆใ„ใพใ™ใ€‚

ใ“ใฎๆ™‚ใ€ๅฏ่ƒฝใช้™ใ‚Šใƒ‰ใ‚ญใƒฅใƒกใƒณใƒˆใ‚‚ใ‚ฝใƒผใ‚นใ‚ณใƒผใƒ‰ใ‚‚ใ€ๅ‚็…งใ™ใ‚‹ๆ™‚ใซใฏไฝฟใฃใฆใ„ใ‚‹ใƒใƒผใ‚ธใƒงใƒณใฎๆŒ‡ใ—ใฆใ„ใ‚‹URLใ‚’ไฝฟ็”จใ™ใ‚‹ใ‚ˆใ†ใซ
ใ—ใฆใ„ใพใ™ใ€‚

ใŸใพใซใƒ‰ใ‚ญใƒฅใƒกใƒณใƒˆใฏ latest ใ—ใ‹ใชใใฆ่ซฆใ‚ใ‚‹ใจใ‹ใ€ใƒชใƒใ‚ธใƒˆใƒชใซใ‚ฟใ‚ฐใŒใชใ„ๆ™‚ใฏไป•ๆ–นใŒใชใ„ใฎใงใ‚ณใƒŸใƒƒใƒˆใƒใƒƒใ‚ทใƒฅใ‚’ๆŒ‡ใ™ใจใ‹
ๅ›ฐใ‚‹ใ“ใจใฏใ‚ใ‚Šใพใ™ใŒใ€ๅฏ่ƒฝใช้™ใ‚Š่จ˜ไบ‹ใ‚’ๆ›ธใ„ใŸๆ™‚ใซไฝฟใฃใŸใƒใƒผใ‚ธใƒงใƒณใ‚’่ฟฝใˆใ‚‹ใ‚ˆใ†ใซๆณจๆ„ใ—ใฆใ„ใพใ™ใ€‚

ใ“ใฎใ‚ใŸใ‚Šใฏๆ›ธใๅง‹ใ‚ใŸๅฝ“ๅˆใฏ็ทฉใใฆใ€ใƒใƒผใ‚ธใƒงใƒณใ‚’ๆ˜Ž่จ˜ใ—ใฆใ„ใชใ„ใจใ‹ใ€ๅ…ฌๅผไปฅๅค–ใฎใƒ–ใƒญใ‚ฐ่จ˜ไบ‹ใชใฉใ‚’ใใฎใพใพ
ๅ‚่€ƒๅ…ˆใจใ—ใฆใ„ใŸใ‚Šใ—ใพใ—ใŸใŒใ€‚

ไปŠใฏใ€ๅ…ฌๅผใฎๆƒ…ๅ ฑใ‚’ๅฟ…ใš็ขบ่ชใ™ใ‚‹ใ‚ˆใ†ใซใ—ใฆใ„ใพใ™ใ€‚

ใŸใพใซใ€ๅ…ฌๅผใฎใƒ‰ใ‚ญใƒฅใƒกใƒณใƒˆใซๆ›ธใ‹ใ‚Œใฆใ„ใชใ„ใ‹ใฃใŸใ‚Šใ€่จ˜่ฟฐใŒๅๅˆ†ใซใชใ„ๆƒ…ๅ ฑใซใคใ„ใฆ่ถณๅ…ƒใ‚’ใ™ใใ‚ใ‚ŒใŸใ‚Šใ—ใฆใ€
ใ€Œใ‚‚ใ†ใกใ‚‡ใฃใจไป–ใ‚‚่ชฟในใฆใŠใ‘ใฐใ‚ˆใ‹ใฃใŸใ‹ใ‚‚ใ€ใจๆ€ใ†ใ“ใจใ‚‚ใ‚ใ‚‹ใฎใงใ™ใŒใ€‚

ๆœ€ๅˆใซ่ฆ‹ใคใ‘ใŸใฎใŒๅ…ฌๅผไปฅๅค–ใฎๆƒ…ๅ ฑใงใ‚‚ใ€ใใ‚ŒใŒๅ…ฌๅผใฎใƒ‰ใ‚ญใƒฅใƒกใƒณใƒˆใซ่ผ‰ใฃใฆใ„ใ‚‹ใ‹ใฉใ†ใ‹ใฏ็ขบ่ชใ™ใ‚‹ใ‚ˆใ†ใซใ—ใฆใ„ใพใ™ใ€‚

็‰นใซๆ›ธใ„ใŸๆ™‚ใฎๆกไปถใ‚„็’ฐๅขƒใ‚’ๆ›ธใๆฎ‹ใ™ใ“ใจใซใ—ใฆใ‹ใ‚‰ๆง‹ๆˆใ‚’่ฆ‹็›ดใ™ใ‚ˆใ†ใซใชใ‚Šใ€ใ“ใ‚Œใ‚‰ใซๅŠ ใˆใฆใ€Œใใ‚‚ใใ‚‚ใชใ‚“ใงใ“ใฎ่จ˜ไบ‹
ๆ›ธใ„ใŸใฎ๏ผŸใ€ใจใ„ใ†ใฎใ‚’ๅฐใ•ใชใ“ใจใงใ‚‚ๆ›ธใใ‚ˆใ†ใซใ—ใŸใฎใŒไปŠใฎๆ›ธใๆ–นใงใ™ใ€‚

ใ‚ใจใฏใ€ๆ›ธใ„ใฆใ„ใฆใ€Œใ“ใ‚Œใ‚‚ๆฐ—ใซใชใ‚‹ใ€ใจๆ€ใฃใŸใ“ใจใ‚’ๆทฑ่ฟฝใ„ใ—ใฆใ€่จ˜ไบ‹ใŒ้•ทใ‚ใซใชใ‚‹ใ“ใจใŒๅข—ใˆใŸๆฐ—ใŒใ—ใพใ™โ€ฆใ€‚

ไป–ใฎใƒ–ใƒญใ‚ฐใ‚ตใƒผใƒ“ใ‚นใ‚„ใ€ใƒ—ใƒฉใƒƒใƒˆใƒ•ใ‚ฉใƒผใƒ ใซ็งปใ‚‰ใชใ„ใฎใฏ๏ผŸ

ใ“ใฎใƒ–ใƒญใ‚ฐใฏใ€ใฏใฆใชใƒ€ใ‚คใ‚ขใƒชใƒผใงๅง‹ใ‚ใฆใ€ไปŠใฏใ€ใฏใฆใชใƒ–ใƒญใ‚ฐใจใ—ใฆๅˆฉ็”จใ•ใ›ใฆใ„ใŸใ ใ„ใฆใ„ใ‚‹ใ‚ใ‘ใงใ™ใŒใ€‚

ใƒ–ใƒญใ‚ฐใ‚’ๆ›ธใใ“ใจใง่‡ชๅˆ†ใฎใƒ–ใƒฉใƒณใƒ‡ใ‚ฃใƒณใ‚ฐใ‚’ใ—ใฆใ„ใ‚‹ใ‚ใ‘ใงใ‚‚ใชใ„ใงใ™ใ—ใ€่จ˜ไบ‹ใ‚’ๆ›ธใ„ใฆๆณจ็›ฎใ‚’้›†ใ‚ใŸใ„ใจใ„ใ†ใ‚ใ‘ใงใ‚‚
ใชใ„ใฎใงใ€็งปใ‚‹็†็”ฑใŒใชใใ€‚

ใชใซใ‚ˆใ‚Šใ€่‡ชๅˆ†ใฎๅ‹‰ๅผทใฎ่จ˜้Œฒใจใ—ใฆๆ›ธใ„ใฆใ„ใ‚‹ใ ใ‘ใชใฎใงใ€ใ‚‚ใ—็งปใ‚‹ใจใ™ใ‚Œใฐ่จ˜ไบ‹ใ”ใจๅ…จ้ƒจ็งปใ‚‰ใชใ„ใจๆ„ๅ‘ณใŒใ‚ใ‚Šใพใ›ใ‚“ใ€‚
ใใ‚Œใ‚‚ๆ‰‹้–“ใงใ™ใ—ใญใ€‚

ๆฐ—ใพใพใซใ€ๆ›ธใใŸใ„ใ‚‚ใฎใŒ่‡ช็”ฑใซๆ›ธใ‘ใ‚‹ๅ ดๆ‰€ใงใ‚ใ‚Œใฐใใ‚Œใงใ„ใ„ใงใ™ใ€‚

ใจใฏใ„ใˆใ€ไป–ใฎๅ ดๆ‰€ใงใ“ใ†ใ„ใ†ๆŠ€่ก“็ณปใฎใƒ–ใƒญใ‚ฐใ€่จ˜ไบ‹ใ‚’ๆ›ธใใŸใใชใ„ใ€ใจใ„ใ†ใ‚ใ‘ใงใ‚‚ใ‚ใ‚Šใพใ›ใ‚“ใ€‚

ใƒ–ใƒญใ‚ฐใ‚’็ถšใ‘ใฆใใฆ่‰ฏใ‹ใฃใŸ๏ผŸ

ๅพŒๆ‚”ใฟใŸใ„ใชใ‚‚ใฎใฏใ€ใพใฃใŸใใ—ใฆใ„ใชใใฆใ€‚

ไป•ไบ‹ใซ็›ดๆŽฅๅฝนใซ็ซ‹ใฃใฆใพใ™ใ‹๏ผŸใจใ„ใ†่ฉฑใ ใจใใ†ใงใ‚‚ใชใ„ใ‚‚ใฎใฏๅคšใ„ใงใ™ใŒใ€ๆƒ…ๅ ฑใฎ่ชฟในๆ–นใ€ๅฎŸ้š›ใซๅ‹•ใ‹ใ—ใฆ็ขบ่ชใ—ใฆใฟใ‚‹ใ€
ใจใ„ใ†็นฐใ‚Š่ฟ”ใ—ใฎ็ตŒ้จ“ใฏใใ‚Œใชใ‚Šใซๆ„ๅ‘ณใŒใ‚ใ‚‹ใ‚“ใ˜ใ‚ƒใชใ„ใ‹ใชใ€ใจๆ€ใฃใฆใ„ใพใ™ใ€‚

ใ“ใฎใƒ–ใƒญใ‚ฐใ‚’ใใฃใ‹ใ‘ใซใ€็Ÿฅใ‚Šๅˆใ„ใซใชใฃใŸๆ–นใ‚‚ใ„ใ‚‰ใฃใ—ใ‚ƒใ„ใพใ™ใ—ใญใ€‚

ใใ‚Œใซใ€ๅนธใ„๏ผŸ่จ˜ไบ‹ใŒๆณจ็›ฎใ‚’้›†ใ‚ใ‚‹ใ“ใจใŒใพใฃใŸใใชใ„ใฎใงใ€้ž้›ฃ๏ผŸใฎใ‚ˆใ†ใชใ‚‚ใฎใ‚‚ๅ—ใ‘ใฆใใพใ›ใ‚“ใงใ—ใŸใ—ใ€‚

ใพใ‚ใ€ใพใฃใŸใใชใซใ‚‚ใชใ„ใ‹ใจใ„ใ†ใจใใ‚“ใชใ“ใจใฏใชใใ€ๅพฎๅฆ™ใซๆ€ใ†ใ‚ณใƒกใƒณใƒˆใŒใคใใจใ‹ใ€ๅฎŸ้š›ใซ่ฆ‹ใŸไบบใ‹ใ‚‰ใ€Œ็Ÿฅใฃใฆใ‚‹ใ“ใจใ—ใ‹
ๆ›ธใ‹ใ‚Œใฆใ„ใชใ„ใ€ใจใ‹่จ€ใ‚ใ‚ŒใŸใ“ใจใจใ‹ใ‚‚ใ‚ใฃใŸใ‚Šใ—ใพใ™ใŒใ€‚

ใ”ใๅฐ‘ๆ•ฐใงใ™ใ—ใ€ใ€Œ่ฆ‹ใชใใฆใ„ใ„ใงใ™ใ‚ˆ๏ผŸใ€ใจใ—ใ‹ๆ€ใ‚ใชใ„ใฎใงใ€‚

ใƒ–ใƒญใ‚ฐใซ็™ปๅ ดใ—ใฆใ„ใชใ„ใ‘ใ‚Œใฉใ€ๅ‹‰ๅผทใ—ใฆใฟใŸใ„ใ‚‚ใฎใฏ๏ผŸ

ใ“ใฎใƒ–ใƒญใ‚ฐใงใฏใ€ใ‚ใ‚‹ๆกไปถใซๅฝ“ใฆใฏใพใฃใŸใ‚‚ใฎใฏใ€่ˆˆๅ‘ณใŒใ‚ใฃใฆใ‚‚ใƒ†ใƒผใƒžใจใ—ใฆใฏๅ–ใ‚Šๆ‰ฑใ‚ใชใ„ใ“ใจใซใ—ใฆใ„ใพใ™ใ€‚

ใใ‚Œใซๅฝ“ใฆใฏใพใ‚‹ใฒใจใคใจใ—ใฆใ€ใ“ใฎใƒ–ใƒญใ‚ฐใซใฏใ‚ฏใƒฉใ‚ฆใƒ‰ใŒ็™ปๅ ดใ—ใพใ›ใ‚“ใ€‚
ใ‚ฏใƒฉใ‚ฆใƒ‰ไปฅๅค–ใฎใ‚‚ใฎใซใคใ„ใฆใฏๆ‰ฑใฃใฆใ„ใชใ„ใ“ใจใ‚’ๆฐ—ใซใ—ใฆใ„ใชใ‹ใฃใŸใฎใงใ™ใŒใ€ใ‚ฏใƒฉใ‚ฆใƒ‰ใซใคใ„ใฆใฏใใ‚Œใงใ„ใ„ใฎใ‹ใช๏ผŸ
ใจใฏๆ™‚ใ€…ๆ€ใฃใฆใ„ใพใ™ใ€‚

ใงใ‚‚ใ€ไปŠๅพŒใ‚‚็™ปๅ ดใ™ใ‚‹ใ“ใจใฏใชใ„ใ‚“ใ ใ‚ใ†ใชใ€ใจใ‚‚ๆ€ใฃใฆใ„ใพใ™ใ€‚

ไปŠๅพŒใฏใฉใ†ใ™ใ‚‹๏ผŸ

ๅค‰ใ‚ใ‚‰ใšใ€ใƒžใ‚คใƒšใƒผใ‚นใซ็ถšใ‘ใ‚‹ใจๆ€ใ„ใพใ™ใ‚ˆใ€‚

ใ“ใ‚Œใ‹ใ‚‰ใ‚‚่ฆ‹ใ‚‹ๆ–นใŒใ„ใ‚‰ใฃใ—ใ‚ƒใ‚‹ใ‚ˆใ†ใงใ‚ใ‚Œใฐใ€ใชใซใ‹ๅฝนใซ็ซ‹ใคใ‚‚ใฎใŒใ‚ใ‚Œใฐ่‰ฏใ„ใชใ€ใจๆ€ใ„ใพใ™ใ€‚

Spring Frameworkใงใ€REQUIREDใชไผๆ’ญใƒฌใƒ™ใƒซใฎใƒˆใƒฉใƒณใ‚ถใ‚ฏใ‚ทใƒงใƒณใŒใƒใ‚นใƒˆใ—ใŸๆ™‚ใฎไพ‹ๅค–ใฎๆ‰ฑใ„ใ‚’็ขบ่ชใ™ใ‚‹

ใ“ใ‚Œใฏใ€ใชใซใ‚’ใ—ใŸใใฆๆ›ธใ„ใŸใ‚‚ใฎ๏ผŸ

Spring Frameworkใ‚’ไฝฟใฃใฆใ„ใ‚‹ใจใ€ใƒˆใƒฉใƒณใ‚ถใ‚ฏใ‚ทใƒงใƒณ็ฎก็†ใ‚’@Transactionalใ‚ขใƒŽใƒ†ใƒผใ‚ทใƒงใƒณใ‚’ไฝฟใฃใฆๅฎฃ่จ€็š„ใซๆ›ธใ„ใฆใ„ใ‚‹ใ“ใจใŒ
ๅคšใ„ใจๆ€ใ„ใพใ™ใ€‚

@Transactionalใ‚’ไฝฟใฃใŸๅ ดๅˆใ€ไพ‹ๅค–๏ผˆใƒ‡ใƒ•ใ‚ฉใƒซใƒˆใงใฏRuntimeExceptionใฎใ‚ตใƒ–ใ‚ฏใƒฉใ‚น๏ผ‰ใŒใ‚นใƒญใƒผใ•ใ‚ŒใŸๆ™‚ใซใƒญใƒผใƒซใƒใƒƒใ‚ฏใ•ใ‚Œใ‚‹
ใ“ใจใซใชใฃใฆใ„ใพใ™ใ€‚ใ“ใ“ใงใ€@Transactionalใ‚ขใƒŽใƒ†ใƒผใ‚ทใƒงใƒณใŒไป˜ไธŽใ•ใ‚ŒใŸใƒกใ‚ฝใƒƒใƒ‰ใŒใƒใ‚นใƒˆใ—ใ€ใ‹ใค้€”ไธญใงไพ‹ๅค–ใ‚’
ๆ•ๆ‰ใ—ใŸๅ ดๅˆใซใฉใ†ใ„ใ†ๆŒ™ๅ‹•ใซใชใ‚‹ใฎใ‹ใ€็ขบ่ชใ—ใฆใฟใŸใ„ใชใจๆ€ใ„ใพใ—ใฆใ€‚

ใƒˆใƒฉใƒณใ‚ถใ‚ฏใ‚ทใƒงใƒณใฎไผๆ’ญใซใคใ„ใฆใฏใ€PROPAGATION_REQUIREDใ‚’ไธปใชๅฏพ่ฑกใซใ—ใฆใ„ใพใ™ใ€‚

Springใฎๅฎฃ่จ€็š„ใƒˆใƒฉใƒณใ‚ถใ‚ฏใ‚ทใƒงใƒณใจใƒˆใƒฉใƒณใ‚ถใ‚ฏใ‚ทใƒงใƒณใฎไผๆ’ญ

Springใฎๅฎฃ่จ€็š„ใƒˆใƒฉใƒณใ‚ถใ‚ฏใ‚ทใƒงใƒณใซ้–ขใ™ใ‚‹ใƒ‰ใ‚ญใƒฅใƒกใƒณใƒˆใจใ—ใฆใฏใ€ใ“ใกใ‚‰ใงใ™ใญใ€‚

Declarative Transaction Management

ใพใŸใ€@Transactionalใ‚ขใƒŽใƒ†ใƒผใ‚ทใƒงใƒณใซใคใ„ใฆใฏใ€ใ“ใกใ‚‰ใซ่จ˜่ผ‰ใ•ใ‚Œใฆใ„ใพใ™ใ€‚

Using @Transactional

ใ“ใ“ใงใ€ใƒˆใƒฉใƒณใ‚ถใ‚ฏใ‚ทใƒงใƒณใฎไผๆ’ญใซ้–ขใ™ใ‚‹ใƒ‰ใ‚ญใƒฅใƒกใƒณใƒˆใ‚’่ชญใ‚“ใงใฟใพใ™ใ€‚

Transaction Propagation

ๆœ€ๅˆใซ็–‘ๅ•ใซๆ›ธใ„ใŸใ“ใจใฎ็ญ”ใˆใŒใ€ๅฎŸใฏใ“ใ“ใซๆ›ธใ„ใฆใ„ใพใ™ใ€‚

Understanding PROPAGATION_REQUIRED

PROPAGATION_REQUIREDใ‚’ไฝฟใฃใŸใƒˆใƒฉใƒณใ‚ถใ‚ฏใ‚ทใƒงใƒณใŒใƒใ‚นใƒˆใ—ใ€ๅ†…ๅดใฎใƒˆใƒฉใƒณใ‚ถใ‚ฏใ‚ทใƒงใƒณใฎใƒญใƒผใƒซใƒใƒƒใ‚ฏใŒ็ขบๅฎšใ—ใŸๅ ดๅˆใฎ
ใ“ใจใŒๆ›ธใ„ใฆใ‚ใ‚Šใพใ™ใ€‚

When the propagation setting is PROPAGATION_REQUIRED, a logical transaction scope is created for each method upon which the setting is applied. Each such logical transaction scope can determine rollback-only status individually, with an outer transaction scope being logically independent from the inner transaction scope. In the case of standard PROPAGATION_REQUIRED behavior, all these scopes are mapped to the same physical transaction. So a rollback-only marker set in the inner transaction scope does affect the outer transactionโ€™s chance to actually commit.

However, in the case where an inner transaction scope sets the rollback-only marker, the outer transaction has not decided on the rollback itself, so the rollback (silently triggered by the inner transaction scope) is unexpected. A corresponding UnexpectedRollbackException is thrown at that point. This is expected behavior so that the caller of a transaction can never be misled to assume that a commit was performed when it really was not. So, if an inner transaction (of which the outer caller is not aware) silently marks a transaction as rollback-only, the outer caller still calls commit. The outer caller needs to receive an UnexpectedRollbackException to indicate clearly that a rollback was performed instead.

ใคใพใ‚Šใ€ๅ†…ๅดใฎใƒˆใƒฉใƒณใ‚ถใ‚ฏใ‚ทใƒงใƒณใฎใƒญใƒผใƒซใƒใƒƒใ‚ฏใŒ็ขบๅฎšใ—ใฆใ„ใ‚‹ใจๅค–ๅดใฎใƒˆใƒฉใƒณใ‚ถใ‚ฏใ‚ทใƒงใƒณใฏUnexpectedRollbackExceptionใ‚’
ใ‚นใƒญใƒผใ™ใ‚‹ใ€ใจใ„ใ†ใ“ใจใซใชใ‚Šใใ†ใงใ™ใ€‚

ๅ†…ๅดใฎใƒˆใƒฉใƒณใ‚ถใ‚ฏใ‚ทใƒงใƒณใ‚’็‹ฌ็ซ‹ใ•ใ›ใŸใ„ๅ ดๅˆใฏใ€PROPAGATION_REQUIRES_NEWใ‚’ไฝฟใ†ใ‚ใ‘ใงใ™ใญใ€‚

Understanding PROPAGATION_REQUIRES_NEW

ไปŠๅ›žใฏใ€ใ“ใ“ใพใงๅซใ‚ใฆ็ขบ่ชใ—ใฆใฟใ‚ˆใ†ใ‹ใชใ€ใจๆ€ใ„ใพใ™ใ€‚

็’ฐๅขƒ

ไปŠๅ›žใฎ็’ฐๅขƒใฏใ€ใ“ใกใ‚‰ใงใ™ใ€‚

$ java --version
openjdk 11.0.11 2021-04-20
OpenJDK Runtime Environment (build 11.0.11+9-Ubuntu-0ubuntu2.20.04)
OpenJDK 64-Bit Server VM (build 11.0.11+9-Ubuntu-0ubuntu2.20.04, mixed mode, sharing)


$ mvn --version
Apache Maven 3.8.1 (05c21c65bdfed0f71a2f2ada8b84da59348c4c5d)
Maven home: $HOME/.sdkman/candidates/maven/current
Java version: 11.0.11, vendor: Ubuntu, runtime: /usr/lib/jvm/java-11-openjdk-amd64
Default locale: ja_JP, platform encoding: UTF-8
OS name: "linux", version: "5.4.0-73-generic", arch: "amd64", family: "unix"

ใƒ‡ใƒผใ‚ฟใƒ™ใƒผใ‚นใฏMySQL 8.0.25ใ‚’ไฝฟใ„ใ€172.17.0.2ใงๅ‹•ไฝœใ—ใฆใ„ใ‚‹ใ‚‚ใฎใจใ—ใพใ™ใ€‚

Spring Bootใƒ—ใƒญใ‚ธใ‚งใ‚ฏใƒˆใ‚’ไฝœๆˆใ™ใ‚‹

ๆœ€ๅˆใซใ€Spring Bootใƒ—ใƒญใ‚ธใ‚งใ‚ฏใƒˆใ‚’ไฝœๆˆใ—ใพใ™ใ€‚Spring Bootใฎใƒใƒผใ‚ธใƒงใƒณใฏ2.5.0ใงใ€ไพๅญ˜้–ขไฟ‚ใซใฏjdbcใจmysqlใ‚’ๅŠ ใˆใพใ™ใ€‚

$ curl -s https://start.spring.io/starter.tgz \
  -d bootVersion=2.5.0 \
  -d javaVersion=11 \
  -d name=transactional-nested-required \
  -d groupId=org.littlewings \
  -d artifactId=transactional-nested-required \
  -d version=0.0.1-SNAPSHOT \
  -d packageName=org.littlewings.spring.jdbc \
  -d dependencies=jdbc,mysql \
  -d baseDir=transactional-nested-required | tar zxvf -


$ cd transactional-nested-required
$ find src -name '*.java' | xargs rm

็”Ÿๆˆใ•ใ‚ŒใŸJavaใ‚ฝใƒผใ‚นใ‚ณใƒผใƒ‰ใฏๅ‰Š้™คใ€‚

Mavenไพๅญ˜้–ขไฟ‚ใชใฉใฏใ€ใ“ใ‚“ใชๆ„Ÿใ˜ใงใ™ใ€‚

 <properties>
        <java.version>11</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

็ขบ่ชใฏใƒ†ใ‚นใƒˆใ‚ณใƒผใƒ‰ใง่กŒใŠใ†ใจๆ€ใ„ใพใ™ใŒใ€@SpringBootApplicationใ‚ขใƒŽใƒ†ใƒผใ‚ทใƒงใƒณใ‚’ไป˜ไธŽใ—ใŸใ‚ฏใƒฉใ‚นใฏไฝœๆˆใ—ใฆ
ใŠใใพใ™ใ€‚

src/main/java/org/littlewings/spring/jdbc/App.java

package org.littlewings.spring.jdbc;

import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class App {
}

application.propertiesใฏใ€ใ“ใ‚“ใช่จญๅฎšใซใ—ใฆใŠใใพใ—ใŸใ€‚

src/main/resources/application.properties

spring.datasource.url=jdbc:mysql://172.17.0.2:3306/practice?characterEncoding=utf-8&characterSetResults=utf-8
spring.datasource.username=kazuhira
spring.datasource.password=password

ใŠ้กŒ

@Transactionalใ‚ขใƒŽใƒ†ใƒผใ‚ทใƒงใƒณใ‚’ไป˜ไธŽใ—ใŸใƒกใ‚ฝใƒƒใƒ‰ใ‚’ๆŒใคใ‚ฏใƒฉใ‚นใ‚’ไฝœๆˆใ—ใ€ใตใคใ†ใซๅ‡ฆ็†ใ‚’ๅฎŒไบ†ใ•ใ›ใŸใ‚Šใ€ไพ‹ๅค–ใ‚’ใ‚นใƒญใƒผใ—ใฆ
ใ‚ญใƒฃใƒƒใƒใ—ใŸใ‚Šใ—ใชใ‹ใฃใŸใ‚Šโ€ฆใจใ„ใใคใ‹ใƒใƒชใ‚จใƒผใ‚ทใƒงใƒณใ‚’ใคใ‘ใฆ็ขบ่ชใ—ใฆใฟใพใ—ใ‚‡ใ†ใ€‚

ใƒˆใƒฉใƒณใ‚ถใ‚ฏใ‚ทใƒงใƒณใฎไผๆ’ญใฏPROPAGATION_REQUIRESใ‹ใ‚‰ๅง‹ใ‚ใ€PROPAGATION_REQUIRES_NEWใ‚‚็น”ใ‚Šไบคใœใฆ
ใ„ใใ‚ˆใ†ใซใ—ใพใ™ใ€‚

ใƒ†ใ‚นใƒˆใ‚ณใƒผใƒ‰ใฎ้››ๅฝข

ใพใšใฏใƒ†ใ‚นใƒˆใ‚ณใƒผใƒ‰ใฎ้››ๅฝขใ‚’ไฝœๆˆใ—ใพใ—ใ‚‡ใ†ใ€‚ใ“ใ‚“ใชๆ„Ÿใ˜ใซใ—ใพใ—ใŸใ€‚

src/test/java/org/littlewings/spring/jdbc/TransactionalTest.java

package org.littlewings.spring.jdbc;

import java.util.List;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.transaction.UnexpectedRollbackException;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

@SpringBootTest
public class TransactionalTest {
    @Autowired
    JdbcTemplate jdbcTemplate;

    @BeforeEach
    public void createTable() {
        jdbcTemplate.execute("drop table if exists sample");

        jdbcTemplate.execute("create table sample(word varchar(25));");
    }

    // ใ“ใ“ใซใ€ใƒ†ใ‚นใƒˆใ‚’ๆ›ธใ
}

ใƒ†ใ‚นใƒˆใ”ใจใซใ€ใƒ†ใƒผใƒ–ใƒซใ‚’DROP ๏ผ† CREATEใ—ใพใ™ใ€‚

Serviceใ‚ฏใƒฉใ‚น

ไปŠๅ›žใฏใ€Serviceใ‚ฏใƒฉใ‚นใ‚’2ใค็”จๆ„ใ—ใพใ™ใ€‚ใใ‚Œใžใ‚Œใ€@Transactionalใ‚ขใƒŽใƒ†ใƒผใ‚ทใƒงใƒณใ‚’ไป˜ไธŽใ—ใŸใƒกใ‚ฝใƒƒใƒ‰ใ‚’ๆŒใกใ€
ๅค–ๅดใฎใƒˆใƒฉใƒณใ‚ถใ‚ฏใ‚ทใƒงใƒณใ€ๅ†…ๅดใฎใƒˆใƒฉใƒณใ‚ถใ‚ฏใ‚ทใƒงใƒณใ‚’่กจ็พใ—ใพใ™ใ€‚

ๅค–ๅดใซ่ฉฒๅฝ“ใ™ใ‚‹ใ‚‚ใฎใฏใ€ใ“ใกใ‚‰ใ€‚JdbcTemplateใจใ€ๅ†…ๅดใฎServiceใ‚ฏใƒฉใ‚นใ‚’ไฝฟ็”จใ—ใพใ™ใ€‚

src/main/java/org/littlewings/spring/jdbc/MyService.java

package org.littlewings.spring.jdbc;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service
public class MyService {
    Logger logger = LoggerFactory.getLogger(MyService.class);

    JdbcTemplate jdbcTemplate;

    NestedService nestedService;

    public MyService(JdbcTemplate jdbcTemplate, NestedService nestedService) {
        this.jdbcTemplate = jdbcTemplate;
        this.nestedService = nestedService;
    }

    // ใƒกใ‚ฝใƒƒใƒ‰ใ‚’ๆ›ธใ
}

MyServiceใ‚ฏใƒฉใ‚นใ‹ใ‚‰ๅ‘ผใณๅ‡บใ•ใ‚Œใ‚‹ใฎใฏใ€ใ“ใกใ‚‰ใ€‚ๅŒใ˜ใใ€JdbcTemplateใ‚’ไฝฟ็”จใ—ใพใ™ใ€‚

src/main/java/org/littlewings/spring/jdbc/NestedService.java

package org.littlewings.spring.jdbc;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service
public class NestedService {
    Logger logger = LoggerFactory.getLogger(NestedService.class);

    JdbcTemplate jdbcTemplate;

    public NestedService(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    // ใƒกใ‚ฝใƒƒใƒ‰ใ‚’ๆ›ธใ
}

ใ“ใ‚Œใ‚‰ใฎใ‚ฏใƒฉใ‚นใจใƒ†ใ‚นใƒˆใ‚ณใƒผใƒ‰ใ‚’ไฝฟใฃใฆใ€็ขบ่ชใ—ใฆใใพใ—ใ‚‡ใ†ใ€‚

ใƒ†ใ‚นใƒˆใ‚ณใƒผใƒ‰ๅดใงใฏใ€ไธŠ่จ˜ใฎMyServiceใ‚ฏใƒฉใ‚นใ‚’ไฝฟ็”จใ—ใพใ™ใ€‚

@SpringBootTest
public class TransactionalTest {
    @Autowired
    JdbcTemplate jdbcTemplate;

    @BeforeEach
    public void createTable() {
        jdbcTemplate.execute("drop table if exists sample");

        jdbcTemplate.execute("create table sample(word varchar(25));");
    }

    @Autowired
    MyService myService;

ใ“ใ“ใ‹ใ‚‰ๅ…ˆใฏใ€MyServiceใ€NestedServiceใ€ใƒ†ใ‚นใƒˆใ‚ณใƒผใƒ‰ใฎ้ †ใงใ„ใ‚ใ„ใ‚ๆ›ธใ„ใฆ็ขบ่ชใ—ใฆใ„ใใพใ™ใ€‚

PROPAGATION_REQUIRES

ใงใฏใ€็ขบ่ชใ—ใฆใ„ใใพใ—ใ‚‡ใ†ใ€‚

ใ‚ณใƒŸใƒƒใƒˆใ™ใ‚‹

ใพใšใฏใตใคใ†ใซใƒˆใƒฉใƒณใ‚ถใ‚ฏใ‚ทใƒงใƒณใŒๅฎŒไบ†ใ™ใ‚‹๏ผˆ๏ผใ‚ณใƒŸใƒƒใƒˆใ™ใ‚‹๏ผ‰ใƒ‘ใ‚ฟใƒผใƒณใ€‚ๆŒ‡ๅฎšใ•ใ‚ŒใŸwordใ‚’็™ป้Œฒใ™ใ‚‹insertๆ–‡ใ‚’ๅฎŸ่กŒใ—ใฆใ€
ใใฎwordใ‚’ไบŒ้‡ใซใ—ใฆๆฌกใฎServiceใ‚ฏใƒฉใ‚นใ‚’ๅ‘ผใณๅ‡บใ—ใพใ™ใ€‚

@Service
public class MyService {

    @Transactional
    public int insertRequired(String word) {
        int result = jdbcTemplate.update("insert into sample(word) values(?)", word);

        return result + nestedService.insertRequired(word + " " + word);
    }

}

ใ‚ฏใƒฉใ‚นใฎๅฎš็พฉใฏใ€ๆŠœ็ฒ‹ใ—ใฆๆ›ธใ„ใฆใ„ใใพใ™ใ€‚

ๅ‘ผใณๅ‡บใ—ๅ…ˆใ€‚

@Service
public class NestedService {

    @Transactional
    public int insertRequired(String word) {
        return jdbcTemplate.update("insert into sample(word) values(?)", word);
    }

}

ใฉใกใ‚‰ใ‚‚ๆ›ดๆ–ฐไปถๆ•ฐใ‚’่ฟ”ใ™ใฎใงใ€ไธกๆ–นใฎๆ›ดๆ–ฐใŒใ†ใพใใ„ใฃใŸๅ ดๅˆใฏๆˆปใ‚Šๅ€คใŒ2ใซใชใ‚Šใพใ™ใญใ€‚

ใƒ†ใ‚นใƒˆใ‚ณใƒผใƒ‰ใ€‚ใƒ‡ใƒผใ‚ฟใŒ็™ป้Œฒใ•ใ‚Œใฆใ„ใ‚‹ใฎใŒ็ขบ่ชใงใใพใ™ใ€‚

    @Test
    public void transactionalNormally() {
        assertThat(myService.insertRequired("Hello!!")).isEqualTo(2);

        assertThat(jdbcTemplate.queryForList("select word from sample order by word", String.class))
                .isEqualTo(List.of("Hello!!", "Hello!! Hello!!"));
    }
ใƒญใƒผใƒซใƒใƒƒใ‚ฏใ™ใ‚‹

ๆฌกใฏใ€ใƒญใƒผใƒซใƒใƒƒใ‚ฏใ™ใ‚‹ใƒ‘ใ‚ฟใƒผใƒณใ€‚

ๅค–ๅดใฎServiceใ‚ฏใƒฉใ‚นใฏใตใคใ†ใงใ™ใŒ

@Service
public class MyService {

    @Transactional
    public int insertRequiredAndNestedThrown(String word) {
        int result = jdbcTemplate.update("insert into sample(word) values(?)", word);

        return result + nestedService.insertRequiredAndThrown(word + " " + word);
    }

}

ๅ†…ๅดใฎServiceใ‚ฏใƒฉใ‚นใงใฏไพ‹ๅค–ใ‚’ใ‚นใƒญใƒผใ—ใพใ™ใ€‚

@Service
public class NestedService {

    @Transactional
    public int insertRequiredAndThrown(String word) {
        jdbcTemplate.update("insert into sample(word) values(?)", word);

        throw new RuntimeException("Oops!!");
    }

}

ใƒ†ใ‚นใƒˆใ‚ณใƒผใƒ‰ๅดใ€‚ใƒญใƒผใƒซใƒใƒƒใ‚ฏใงใ™ใญใ€‚

    @Test
    public void transactionalNestedFailed() {
        assertThatThrownBy(() -> myService.insertRequiredAndNestedThrown("Hello!!"))
                .isInstanceOf(RuntimeException.class)
                .hasMessage("Oops!!");

        assertThat(jdbcTemplate.queryForList("select word from sample order by word", String.class))
                .isEmpty();
    }
ๅ†…ๅดใฎServiceใ‚ฏใƒฉใ‚นใŒไพ‹ๅค–ใ‚’ใ‚นใƒญใƒผใ—ใ€ๅค–ๅดใฎServiceใ‚ฏใƒฉใ‚นๅ†…ใงๆ•ๆ‰ใ™ใ‚‹

็ถšใ„ใฆใฏใ€ๅ†…ๅดใฎServiceใ‚ฏใƒฉใ‚นใฎใƒกใ‚ฝใƒƒใƒ‰ใŒไพ‹ๅค–ใ‚’ใ‚นใƒญใƒผใ—ใฆใ€ๅค–ๅดใฎServiceใ‚ฏใƒฉใ‚นใงใใฎไพ‹ๅค–ใ‚’่ฃœ่ถณใ™ใ‚‹ใƒ‘ใ‚ฟใƒผใƒณใ€‚

ใคใพใ‚Šใ€ๅค–ๅดใฎServiceใ‚ฏใƒฉใ‚นใฏใ“ใ‚“ใชๆ„Ÿใ˜ใงใ™ใ€‚

@Service
public class MyService {

    @Transactional
    public int insertRequiredAndNestedThrownAndCatch(String word) {
        int result = jdbcTemplate.update("insert into sample(word) values(?)", word);

        try {
            nestedService.insertRequiredAndThrown(word + " " + word);
        } catch (RuntimeException e) {
            logger.error("insert failed", e);
        }

        return result;
    }

}

ๅ†…ๅดใฎServiceใ‚ฏใƒฉใ‚นใฏใ€ๅ…ˆใปใฉใจๅŒใ˜ใงใ™ใ€‚

@Service
public class NestedService {

    @Transactional
    public int insertRequiredAndThrown(String word) {
        jdbcTemplate.update("insert into sample(word) values(?)", word);

        throw new RuntimeException("Oops!!");
    }

}

ใƒ†ใ‚นใƒˆใ‚ณใƒผใƒ‰ใฏๅค–ๅดใฎServiceใ‚ฏใƒฉใ‚นใ‹ใ‚‰ไพ‹ๅค–ใŒใ‚นใƒญใƒผใ•ใ‚Œใชใ„โ€ฆใจๆ€ใ„ใใ‚„ใ€UnexpectedRollbackExceptionใŒใ‚นใƒญใƒผใ•ใ‚Œใ‚‹
ใ“ใจใซใชใ‚Šใพใ™ใ€‚

ใ“ใ‚“ใชใ“ใจใ‚’่จ€ใ‚ใ‚Œใคใคใ€‚

Transaction rolled back because it has been marked as rollback-only

    @Test
    public void transactionalNestedFailedAndCatch() {
        assertThatThrownBy(() -> myService.insertRequiredAndNestedThrownAndCatch("Hello!!"))
                .isInstanceOf(UnexpectedRollbackException.class)
                .hasMessage("Transaction rolled back because it has been marked as rollback-only");

        assertThat(jdbcTemplate.queryForList("select word from sample order by word", String.class))
                .isEmpty();
    }

ใƒ‰ใ‚ญใƒฅใƒกใƒณใƒˆ้€šใ‚Šใงใ™ใญใ€‚

ๅ†…ๅดใฎServiceใ‚ฏใƒฉใ‚นใงไพ‹ๅค–ใฏ็™บ็”Ÿใ™ใ‚‹ใ‚‚ใฎใฎใ€ใƒกใ‚ฝใƒƒใƒ‰ใ‚’ไพ‹ๅค–ใงๆŠœใ‘ใชใ„

ใชใซใ‚’่จ€ใฃใฆใ„ใ‚‹ใ‹ใจใ„ใ†ใจใ€ๅ†…ๅดใฎServiceใ‚ฏใƒฉใ‚นใงไพ‹ๅค–ใฏ็™บ็”Ÿใ™ใ‚‹ใ‚‚ใฎใฎใ€ใƒˆใƒฉใƒณใ‚ถใ‚ฏใ‚ทใƒงใƒณใฎๅขƒ็•Œใจใชใ‚‹ใƒกใ‚ฝใƒƒใƒ‰ใฏ
ไพ‹ๅค–ใง่„ฑๅ‡บใ—ใชใ„ใ€ใจใ€‚

ๅค–ๅดใฎServiceใ‚ฏใƒฉใ‚นใฏใ€ใ“ใ‚“ใชๆ„Ÿใ˜ใงใ™ใ€‚

@Service
public class MyService {

    @Transactional
    public int insertRequiredAndNestedIgnoreFailed(String word) {
        int result = jdbcTemplate.update("insert into sample(word) values(?)", word);

        return result + nestedService.insertRequiredAndIgnoreFailed(word + " " + word);
    }

}

ๅ†…ๅดใฎServiceใ‚ฏใƒฉใ‚นใฏ่ชคใฃใŸSQLๆ–‡ใ‚’ๅฎŸ่กŒใ•ใ›ใฆไพ‹ๅค–ใŒ็™บ็”Ÿใ—ใพใ™ใŒใ€ใƒกใ‚ฝใƒƒใƒ‰่‡ชไฝ“ใฏไพ‹ๅค–ใงใฏๆŠœใ‘ใพใ›ใ‚“ใ€‚

@Service
public class NestedService {

    @Transactional
    public int insertRequiredAndIgnoreFailed(String word) {
        try {
            // ๆง‹ๆ–‡่ชคใ‚ŠใงๅฎŸ่กŒใซๅคฑๆ•—ใ™ใ‚‹SQL
            jdbcTemplate.update("insert into sample(word) v(?)", word);
        } catch (RuntimeException e) {
            logger.error("sql error", e);
        }

        return 0;
    }

}

ใƒ†ใ‚นใƒˆใ‚ณใƒผใƒ‰ใงใฏใ€ๅค–ๅดใฎServiceใ‚ฏใƒฉใ‚นใฎๅ‡ฆ็†็ตๆžœใ ใ‘ใŒๅๆ˜ ๏ผˆ๏ผใ‚ณใƒŸใƒƒใƒˆ๏ผ‰ใ•ใ‚Œใฆใ„ใ‚‹ใ“ใจใŒ็ขบ่ชใงใใพใ™ใ€‚

    @Test
    public void transactionalNestedIgnoreFailed() {
        assertThat(myService.insertRequiredAndNestedIgnoreFailed("Hello!!")).isEqualTo(1);

        assertThat(jdbcTemplate.queryForList("select word from sample order by word", String.class))
                .isEqualTo(List.of("Hello!!"));
    }

ไบˆๆƒณใงใใ‚‹่ฉฑใงใฏใ‚ใ‚Šใพใ™ใŒใ€ใƒˆใƒฉใƒณใ‚ถใ‚ฏใ‚ทใƒงใƒณใฎๅขƒ็•Œใ‚’ไพ‹ๅค–ใงๆŠœใ‘ใฆใ„ใพใ›ใ‚“ใ‹ใ‚‰ใญใ€‚

ใคใพใ‚Šใ€ใƒˆใƒฉใƒณใ‚ถใ‚ฏใ‚ทใƒงใƒณใฎไผๆ’ญใŒPROPAGATION_REQUIREDใฎ็ฉใฟ้‡ใญใจใชใ‚Šใˆใ‚‹ๅ ดๅˆใฏใ€ไธญ้€”ๅŠ็ซฏใชใจใ“ใ‚ใง
ไพ‹ๅค–ใ‚’ๆ•ใพใˆใšใซใƒˆใƒฉใƒณใ‚ถใ‚ฏใ‚ทใƒงใƒณใฎๅขƒ็•Œๅค–ใงๆ•ๆ‰ใ™ใ‚‹ใ‹ใ€ใƒˆใƒฉใƒณใ‚ถใ‚ฏใ‚ทใƒงใƒณใฎๅขƒ็•Œใ‚’่ทจใŒใชใ„ใ†ใกใซๆ•ๆ‰ใ™ใ‚‹ใ“ใจใ€ใจใ„ใ†
ๆ„Ÿใ˜ใซใ—ใŸๆ–นใŒ่‰ฏใ•ใใ†ใงใ™ใญใ€‚

ใ‚ฝใƒผใ‚นใ‚ณใƒผใƒ‰ใ‹ใ‚‰็ขบ่ชใ™ใ‚‹

ใ“ใฎใ‚ใŸใ‚Šใฎๅ‹•ไฝœใ‚’ใ€ใ‚ฝใƒผใ‚นใ‚ณใƒผใƒ‰ไธŠใงใ‚‚็ขบ่ชใ—ใฆใฟใพใ—ใ‚‡ใ†ใ€‚

ใƒˆใƒฉใƒณใ‚ถใ‚ฏใ‚ทใƒงใƒณๅขƒ็•Œใจใชใ‚‹ใƒกใ‚ฝใƒƒใƒ‰ใ‚’ไพ‹ๅค–ใงๆŠœใ‘ใŸๆ™‚็‚นใงใ€ใƒญใƒผใƒซใƒใƒƒใ‚ฏใฎใƒžใƒผใ‚ฏใŒ่กŒใ‚ใ‚Œใพใ™ใ€‚

https://github.com/spring-projects/spring-framework/blob/v5.3.7/spring-tx/src/main/java/org/springframework/transaction/support/AbstractPlatformTransactionManager.java#L844

ResourceHolderSupportใงใฎrollbackOnlyใŒtrueใซ่จญๅฎšใ•ใ‚Œใ‚‹ใฎใŒใ€ใใฎใƒžใƒผใ‚ฏใงใ™ใญใ€‚

https://github.com/spring-projects/spring-framework/blob/v5.3.7/spring-tx/src/main/java/org/springframework/transaction/support/ResourceHolderSupport.java#L67-L69

ใ“ใฎ็Šถๆ…‹ใซใชใ‚‹ใจใ€ๅค–ๅดใฎใƒˆใƒฉใƒณใ‚ถใ‚ฏใ‚ทใƒงใƒณๅขƒ็•Œใ‚’ใ‚ณใƒŸใƒƒใƒˆใ—ใ‚ˆใ†ใจใ—ใŸใ‚ฟใ‚คใƒŸใƒณใ‚ฐใงใ€ใƒญใƒผใƒซใƒใƒƒใ‚ฏใ‚’่กŒใ†ใ‚ˆใ†ใซใƒžใƒผใ‚ฏใ•ใ‚Œใฆ
ใ„ใ‚‹ใ“ใจใŒๆคœๅ‡บใ•ใ‚Œใพใ™ใ€‚

https://github.com/spring-projects/spring-framework/blob/v5.3.7/spring-tx/src/main/java/org/springframework/transaction/support/AbstractPlatformTransactionManager.java#L703-L709

ใใ—ใฆใ€UnexpectedRollbackExceptionใŒใ‚นใƒญใƒผใ•ใ‚Œใพใ™ใ€ใจใ€‚

https://github.com/spring-projects/spring-framework/blob/v5.3.7/spring-tx/src/main/java/org/springframework/transaction/support/AbstractPlatformTransactionManager.java#L869-L872

ใ“ใกใ‚‰ใŒใ€ใ‚ฝใƒผใ‚นใ‚ณใƒผใƒ‰ไธŠใงใฎ็ขบ่ชใงใ™ใญใ€‚

ใ‚ใจใฏใ€ใ‚‚ใ†ๅฐ‘ใ—ใƒใƒชใ‚จใƒผใ‚ทใƒงใƒณใ‚’็ขบ่ชใ—ใฆใฟใพใ—ใ‚‡ใ†ใ€‚

PROPAGATION_REQUIRESใจPROPAGATION_REQUIRES_NEWใฎ็ต„ใฟๅˆใ‚ใ›

ไปŠๅบฆใฏใ€ๅค–ๅดใ‚’PROPAGATION_REQUIRESใ€ๅ†…ๅดใ‚’PROPAGATION_REQUIRES_NEWใซใ—ใฆใฟใพใ™ใ€‚

ๅค–ๅดใจๅ†…ๅดใงใ€ใƒˆใƒฉใƒณใ‚ถใ‚ฏใ‚ทใƒงใƒณใŒๅˆฅใซใชใ‚Šใพใ™ใญใ€‚

ใ‚ณใƒŸใƒƒใƒˆใ™ใ‚‹

ๅค–ๅดใฎServiceใ‚ฏใƒฉใ‚นใ€‚

@Service
public class MyService {

    @Transactional
    public int insertRequiredAndNestedNew(String word) {
        int result = jdbcTemplate.update("insert into sample(word) values(?)", word);

        return result + nestedService.insertRequiredNew(word + " " + word);
    }

}

ๅ†…ๅดใฎServiceใ‚ฏใƒฉใ‚นใ€‚ใƒˆใƒฉใƒณใ‚ถใ‚ฏใ‚ทใƒงใƒณใฎไผๆ’ญใƒฌใƒ™ใƒซใŒใ€REQUIRES_NEWใงใ™ใ€‚

@Service
public class NestedService {

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public int insertRequiredNew(String word) {
        return jdbcTemplate.update("insert into sample(word) values(?)", word);
    }

}

ใจใฏใ„ใˆใ€ใ‚ณใƒŸใƒƒใƒˆใ™ใ‚‹ใฎใง็ตๆžœใฏใพใ‚ใตใคใ†ใงใ™ใ€‚

    @Test
    public void transactionalNormallyNestedNew() {
        assertThat(myService.insertRequiredAndNestedNew("Hello!!")).isEqualTo(2);

        assertThat(jdbcTemplate.queryForList("select word from sample order by word", String.class))
                .isEqualTo(List.of("Hello!!", "Hello!! Hello!!"));
    }
ใƒญใƒผใƒซใƒใƒƒใ‚ฏใ™ใ‚‹

็ถšใ„ใฆใ€ใƒญใƒผใƒซใƒใƒƒใ‚ฏใ€‚ใ“ใกใ‚‰ใ‚‚ๅค‰ใ‚ใฃใŸใ“ใจใฏใ‚ใ‚Šใพใ›ใ‚“ใ€‚

ๅค–ๅดใฎServiceใ‚ฏใƒฉใ‚นใ€‚

@Service
public class MyService {

    @Transactional
    public int insertRequiredAndNestedNewThrown(String word) {
        int result = jdbcTemplate.update("insert into sample(word) values(?)", word);

        return result + nestedService.insertRequiredNewAndThrown(word + " " + word);
    }

}

ๅ†…ๅดใฎServiceใ‚ฏใƒฉใ‚นใ€‚

@Service
public class NestedService {

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public int insertRequiredNewAndThrown(String word) {
        jdbcTemplate.update("insert into sample(word) values(?)", word);

        throw new RuntimeException("Oops!!");
    }

}

ไธกๆ–นใฎใƒกใ‚ฝใƒƒใƒ‰ใ‚’ไพ‹ๅค–ใงๆŠœใ‘ใ‚‹ใฎใงใ€ใƒญใƒผใƒซใƒใƒƒใ‚ฏใ—ใพใ™ใ€‚

    @Test
    public void transactionalNestedNewFailed() {
        assertThatThrownBy(() -> myService.insertRequiredAndNestedNewThrown("Hello!!"))
                .isInstanceOf(RuntimeException.class)
                .hasMessage("Oops!!");

        assertThat(jdbcTemplate.queryForList("select word from sample order by word", String.class))
                .isEmpty();
    }
ๅ†…ๅดใฎServiceใ‚ฏใƒฉใ‚นใŒไพ‹ๅค–ใ‚’ใ‚นใƒญใƒผใ—ใ€ๅค–ๅดใฎServiceใ‚ฏใƒฉใ‚นๅ†…ใงๆ•ๆ‰ใ™ใ‚‹

ใ“ใกใ‚‰ใฏใ€PROPAGATION_REQUIREDใฎๆ™‚ใจๅค‰ๅŒ–ใŒใ‚ใ‚Šใพใ™ใ€‚

ๅค–ๅดใฎServiceใ‚ฏใƒฉใ‚นใ€‚ๅ†…ๅดใฎServiceใ‚ฏใƒฉใ‚นใŒใ‚นใƒญใƒผใ—ใŸไพ‹ๅค–ใ‚’ๆ•ๆ‰ใ—ใพใ™ใ€‚

@Service
public class MyService {

    @Transactional
    public int insertRequiredAndNestedNewThrownAndCatch(String word) {
        int result = jdbcTemplate.update("insert into sample(word) values(?)", word);

        try {
            nestedService.insertRequiredNewAndThrown(word + " " + word);
        } catch (RuntimeException e) {
            logger.error("insert failed", e);
        }

        return result;
    }

}

ๅ†…ๅดใฎServiceใ‚ฏใƒฉใ‚นใฏใ€ๅ…ˆใปใฉใจๅŒใ˜ใงใ™ใ€‚

@Service
public class NestedService {

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public int insertRequiredNewAndThrown(String word) {
        jdbcTemplate.update("insert into sample(word) values(?)", word);

        throw new RuntimeException("Oops!!");
    }

}

ใƒ†ใ‚นใƒˆใ‚ณใƒผใƒ‰ใ€‚PROPAGATION_REQUIREDใŒใƒใ‚นใƒˆใ—ใฆใ„ใ‚‹ๆ™‚ใจใฏ็•ฐใชใ‚Šใ€UnexpectedRollbackExceptionใฏใ‚นใƒญใƒผใ•ใ‚Œใš
ๅค–ๅดใฎใƒˆใƒฉใƒณใ‚ถใ‚ฏใ‚ทใƒงใƒณใฏใ‚ณใƒŸใƒƒใƒˆใ•ใ‚Œใพใ™ใ€‚

    @Test
    public void transactionalNestedNewFailedAndCatch() {
        assertThat(myService.insertRequiredAndNestedNewThrownAndCatch("Hello!!")).isEqualTo(1);

        assertThat(jdbcTemplate.queryForList("select word from sample order by word", String.class))
                .isEqualTo(List.of("Hello!!"));  // ไพ‹ๅค–ใซใชใ‚‰ใชใ„
    }

ๅˆฅใ€…ใฎใƒˆใƒฉใƒณใ‚ถใ‚ฏใ‚ทใƒงใƒณใซใชใฃใฆใ„ใ‚‹ใฎใงใ€ใ“ใ†ใชใ‚Šใพใ™ใ‚ˆใญใ€‚

ๅ†…ๅดใฎServiceใ‚ฏใƒฉใ‚นใงไพ‹ๅค–ใฏ็™บ็”Ÿใ™ใ‚‹ใ‚‚ใฎใฎใ€ใƒกใ‚ฝใƒƒใƒ‰ใ‚’ไพ‹ๅค–ใงๆŠœใ‘ใชใ„

ใ“ใกใ‚‰ใ‚‚่ฉฆใ—ใฆใฟใพใ™ใ€‚

ๅค–ๅดใฎServiceใ‚ฏใƒฉใ‚นใ€‚

@Service
public class MyService {

    @Transactional
    public int insertRequiredAndNestedNewIgnoreFailed(String word) {
        int result = jdbcTemplate.update("insert into sample(word) values(?)", word);

        return result + nestedService.insertRequiredNewAndIgnoreFailed(word + " " + word);
    }

}

ๅ†…ๅดใฎServiceใ‚ฏใƒฉใ‚นใ€‚

@Service
public class NestedService {

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public int insertRequiredNewAndIgnoreFailed(String word) {
        try {
            // ๆง‹ๆ–‡่ชคใ‚ŠใงๅฎŸ่กŒใซๅคฑๆ•—ใ™ใ‚‹SQL
            jdbcTemplate.update("insert into sample(word) v(?)", word);
        } catch (RuntimeException e) {
            logger.error("sql error", e);
        }

        return 0;
    }

}

ใƒ†ใ‚นใƒˆใ‚ณใƒผใƒ‰ใ€‚ใ“ใกใ‚‰ใซใคใ„ใฆใฏใ€ใใ‚Œใžใ‚Œๅˆฅใฎใƒˆใƒฉใƒณใ‚ถใ‚ฏใ‚ทใƒงใƒณใŒใ‚ณใƒŸใƒƒใƒˆใ•ใ‚ŒใŸใ ใ‘ใ€ใจใชใ‚Šใพใ™ใ€‚

    @Test
    public void transactionalNestedNewIgnoreFailed() {
        assertThat(myService.insertRequiredAndNestedNewIgnoreFailed("Hello!!")).isEqualTo(1);

        assertThat(jdbcTemplate.queryForList("select word from sample order by word", String.class))
                .isEqualTo(List.of("Hello!!"));
    }

ใใ‚Šใ‚ƒใ‚ใ€ใใ†ใชใ‚Šใพใ™ใ‚ˆใญใ€ใจใ€‚

ไธกๆ–นใจใ‚‚PROPAGATION_REQUIRES_NEWใซใ™ใ‚‹

ไปŠๅ›žใฎ็ขบ่ชๆ–นๆณ•ใงใ“ใ‚Œใ‚’ใ‚„ใ‚‹ๆ„ๅ‘ณใฏใชใ„ๆฐ—ใŒใ—ใพใ™ใŒใ€็ถฒ็พ…็š„ใชๆ„ๅ‘ณใงใฏไธ€ๅฟœโ€ฆใจใ„ใ†ใ“ใจใงใ€‚

ใ‚ฝใƒผใ‚นใ‚ณใƒผใƒ‰ใจ็ตๆžœใ ใ‘่ผ‰ใ›ใพใ™ใ€‚

PROPAGATION_REQUIRESใ€PROPAGATION_REQUIRES_NEWใฎ็ต„ใฟๅˆใ‚ใ›ใจๅŒใ˜็ตๆžœใซใชใ‚Šใพใ™ใ€‚ใใ‚Œใžใ‚ŒใŒ
็‹ฌ็ซ‹ใ™ใ‚‹ใ“ใจใ‚’ๆ˜Ž็คบใ—ใฆใ„ใ‚‹ใ ใ‘ใชใฎใงใ€‚

ใ‚ณใƒŸใƒƒใƒˆใ™ใ‚‹

ๅค–ๅดใฎServiceใ‚ฏใƒฉใ‚นใ€‚

@Service
public class MyService {

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public int insertRequiredNewAndNestedNew(String word) {
        int result = jdbcTemplate.update("insert into sample(word) values(?)", word);

        return result + nestedService.insertRequiredNew(word + " " + word);
    }

}

ๅ†…ๅดใฎServiceใ‚ฏใƒฉใ‚นใ€‚

@Service
public class NestedService {

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public int insertRequiredNew(String word) {
        return jdbcTemplate.update("insert into sample(word) values(?)", word);
    }

}

ใƒ†ใ‚นใƒˆใ‚ณใƒผใƒ‰ใ€‚

    @Test
    public void transactionalNormallyNewNestedNew() {
        assertThat(myService.insertRequiredNewAndNestedNew("Hello!!")).isEqualTo(2);

        assertThat(jdbcTemplate.queryForList("select word from sample order by word", String.class))
                .isEqualTo(List.of("Hello!!", "Hello!! Hello!!"));
    }
ใƒญใƒผใƒซใƒใƒƒใ‚ฏใ™ใ‚‹

ๅค–ๅดใฎServiceใ‚ฏใƒฉใ‚นใ€‚

@Service
public class MyService {

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public int insertRequiredNewAndNestedNewThrown(String word) {
        int result = jdbcTemplate.update("insert into sample(word) values(?)", word);

        return result + nestedService.insertRequiredNewAndThrown(word + " " + word);
    }

}

ๅ†…ๅดใฎServiceใ‚ฏใƒฉใ‚นใ€‚

@Service
public class NestedService {

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public int insertRequiredNewAndThrown(String word) {
        jdbcTemplate.update("insert into sample(word) values(?)", word);

        throw new RuntimeException("Oops!!");
    }

}

ใƒ†ใ‚นใƒˆใ‚ณใƒผใƒ‰ใ€‚

    @Test
    public void transactionalNewNestedNewFailed() {
        assertThatThrownBy(() -> myService.insertRequiredNewAndNestedNewThrown("Hello!!"))
                .isInstanceOf(RuntimeException.class)
                .hasMessage("Oops!!");

        assertThat(jdbcTemplate.queryForList("select word from sample order by word", String.class))
                .isEmpty();
    }
ๅ†…ๅดใฎServiceใ‚ฏใƒฉใ‚นใŒไพ‹ๅค–ใ‚’ใ‚นใƒญใƒผใ—ใ€ๅค–ๅดใฎServiceใ‚ฏใƒฉใ‚นๅ†…ใงๆ•ๆ‰ใ™ใ‚‹

ๅค–ๅดใฎServiceใ‚ฏใƒฉใ‚นใ€‚

@Service
public class MyService {

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public int insertRequiredNewAndNestedNewThrownAndCatch(String word) {
        int result = jdbcTemplate.update("insert into sample(word) values(?)", word);

        try {
            nestedService.insertRequiredNewAndThrown(word + " " + word);
        } catch (RuntimeException e) {
            logger.error("insert failed", e);
        }

        return result;
    }

}

ๅ†…ๅดใฎServiceใ‚ฏใƒฉใ‚นใ€‚

@Service
public class NestedService {

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public int insertRequiredNewAndThrown(String word) {
        jdbcTemplate.update("insert into sample(word) values(?)", word);

        throw new RuntimeException("Oops!!");
    }

}

ใƒ†ใ‚นใƒˆใ‚ณใƒผใƒ‰ใ€‚

    @Test
    public void transactionalNewNestedNewFailedAndCatch() {
        assertThat(myService.insertRequiredNewAndNestedNewThrownAndCatch("Hello!!")).isEqualTo(1);

        assertThat(jdbcTemplate.queryForList("select word from sample order by word", String.class))
                .isEqualTo(List.of("Hello!!"));  // ไพ‹ๅค–ใซใชใ‚‰ใชใ„
    }
ๅ†…ๅดใฎServiceใ‚ฏใƒฉใ‚นใงไพ‹ๅค–ใฏ็™บ็”Ÿใ™ใ‚‹ใ‚‚ใฎใฎใ€ใƒกใ‚ฝใƒƒใƒ‰ใ‚’ไพ‹ๅค–ใงๆŠœใ‘ใชใ„

ๅค–ๅดใฎServiceใ‚ฏใƒฉใ‚นใ€‚

@Service
public class MyService {

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public int insertRequiredNewAndNestedNewIgnoreFailed(String word) {
        int result = jdbcTemplate.update("insert into sample(word) values(?)", word);

        return result + nestedService.insertRequiredNewAndIgnoreFailed(word + " " + word);
    }

}

ๅ†…ๅดใฎServiceใ‚ฏใƒฉใ‚นใ€‚

@Service
public class NestedService {

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public int insertRequiredNewAndIgnoreFailed(String word) {
        try {
            // ๆง‹ๆ–‡่ชคใ‚ŠใงๅฎŸ่กŒใซๅคฑๆ•—ใ™ใ‚‹SQL
            jdbcTemplate.update("insert into sample(word) v(?)", word);
        } catch (RuntimeException e) {
            logger.error("sql error", e);
        }

        return 0;
    }

}

ใƒ†ใ‚นใƒˆใ‚ณใƒผใƒ‰ใ€‚

    @Test
    public void transactionalNewNestedNewIgnoreFailed() {
        assertThat(myService.insertRequiredNewAndNestedNewIgnoreFailed("Hello!!")).isEqualTo(1);

        assertThat(jdbcTemplate.queryForList("select word from sample order by word", String.class))
                .isEqualTo(List.of("Hello!!"));
    }

ใพใจใ‚

Spring Frameworkใ‚’ไฝฟใฃใŸๆ™‚ใฎใ€REQUIREDใชไผๆ’ญใƒฌใƒ™ใƒซใฎใƒˆใƒฉใƒณใ‚ถใ‚ฏใ‚ทใƒงใƒณใŒใƒใ‚นใƒˆใ—ใŸๆ™‚ใซใ€ไพ‹ๅค–ใ‚’ใฉใ†ๆ‰ฑใ†ใ‹ใง
ใฉใฎใ‚ˆใ†ใชๆŒ™ๅ‹•ใ™ใ‚‹ใฎใ‹ใ€็ขบ่ชใ—ใฆใฟใพใ—ใŸใ€‚

ใƒ‰ใ‚ญใƒฅใƒกใƒณใƒˆใซ็ญ”ใˆใฏๆ›ธใ„ใฆใ‚ใ‚‹ใฎใงใ™ใŒใ€REQUIREDใŒใƒใ‚นใƒˆใ—ใฆใ„ใ‚‹ๆ™‚ใซไธญ้€”ๅŠ็ซฏใชๅ ดๆ‰€ใงไพ‹ๅค–ใ‚’ๆ•ๆ‰ใ—ใŸใ‚Šใ™ใ‚‹ใจ
ๅŽ„ไป‹ใชใ“ใจใซใชใ‚Šใใ†ใงใ™ใญใ€‚

ไผๆ’ญใƒฌใƒ™ใƒซใ‚’ใ‚ใ‘ใ‚‹ใ‹ใ€ไพ‹ๅค–ใ‚’ๆ•ใพใˆใ‚‹ใ‹ใ€ใƒˆใƒฉใƒณใ‚ถใ‚ฏใ‚ทใƒงใƒณๅขƒ็•Œใ‚’ๆŠœใ‘ใฆใ—ใพใ†ใ‹ใ€ใ“ใฎใ‚ใŸใ‚Šใ‚’ใกใ‚ƒใ‚“ใจ่€ƒใˆใฆ
ๆ›ธใ‹ใชใ„ใจใ„ใ‘ใชใ„ใงใ™ใญใ€‚