{"id":460683,"date":"2025-05-23T03:02:59","date_gmt":"2025-05-23T03:02:59","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=460683"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=460683","title":{"rendered":"<span>\u0418\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u043e\u0435 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0432 Spring Boot: SourceCraft + Amplicode + Docker Compose Starter \u0432 \u0434\u0435\u043b\u0435<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u041a\u043e\u0433\u0434\u0430 \u043e\u0434\u043d\u0438\u0445 \u044e\u043d\u0438\u0442-\u0442\u0435\u0441\u0442\u043e\u0432 \u0443\u0436\u0435 \u043d\u0435\u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e, \u043d\u0430 \u0441\u0446\u0435\u043d\u0443 \u0432\u044b\u0445\u043e\u0434\u044f\u0442 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0435. \u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u043e\u0442 \u043a\u043e\u043c\u0430\u043d\u0434\u044b <a href=\"https:\/\/amplicode.ru\/?utm_source=habr&amp;utm_medium=article&amp;utm_campaign=integration-testing-part1\">Amplicode<\/a> \u043c\u044b \u043f\u043e\u043a\u0430\u0436\u0435\u043c, \u043a\u0430\u043a \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c REST API \u0432 Spring Boot \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0433\u043e \u0441\u0442\u0435\u043a\u0430: \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f \u0442\u0435\u0441\u0442\u043e\u0432 \u0447\u0435\u0440\u0435\u0437 Amplicode, \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0437\u0430\u043f\u0443\u0441\u043a \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e Docker Compose Starter \u0438 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0438 \u0441\u043e \u0441\u0442\u043e\u0440\u043e\u043d\u044b LLM-\u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u043e\u0442 \u042f\u043d\u0434\u0435\u043a\u0441\u0430.<\/p>\n<hr\/>\n<p>\u0421\u0442\u0430\u0442\u044c\u044f \u0442\u0430\u043a\u0436\u0435 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u0430 \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435 \u0432\u0438\u0434\u0435\u043e \u043d\u0430\u00a0<a href=\"https:\/\/youtu.be\/suqpDTeHqSY\">YouTube<\/a>,\u00a0<a href=\"https:\/\/vk.com\/video-222549074_456239152\">VK \u0412\u0438\u0434\u0435\u043e<\/a>\u00a0\u0438\u00a0<a href=\"https:\/\/rutube.ru\/video\/130fb4f1b441e8416237d2315e798a6d\/\">RUTUBE<\/a>, \u0442\u0430\u043a \u0447\u0442\u043e \u043c\u043e\u0436\u043d\u043e \u0438 \u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c, \u0438 \u0447\u0438\u0442\u0430\u0442\u044c \u2014 \u043a\u0430\u043a \u0432\u0430\u043c \u0443\u0434\u043e\u0431\u043d\u0435\u0435!\u00a0<\/p>\n<div class=\"tm-iframe_temp\" data-src=\"https:\/\/embedd.srv.habr.com\/iframe\/682b1143488f69a80822b07e\" data-style=\"\" id=\"682b1143488f69a80822b07e\" width=\"\"><\/div>\n<hr\/>\n<p>\u0422\u0435\u043c\u0430 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043e\u0434\u043d\u0430 \u0438\u0437 \u0432\u0430\u0436\u043d\u0435\u0439\u0448\u0438\u0445 \u0434\u043b\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0438\u043c\u0435\u043d\u043d\u043e \u0431\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u044f \u0442\u0435\u0441\u0442\u0430\u043c \u043c\u044b \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u043f\u0440\u0435\u0438\u043c\u0443\u0449\u0435\u0441\u0442\u0432\u0430:<\/p>\n<ol>\n<li>\n<p>\u0423\u0432\u0435\u0440\u0435\u043d\u043d\u043e\u0441\u0442\u044c \u0432 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u0440\u0430\u0431\u043e\u0442\u044b \u043a\u043e\u0434\u0430<\/p>\n<\/li>\n<li>\n<p>\u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0433\u043e \u0440\u0435\u0444\u0430\u043a\u0442\u043e\u0440\u0438\u043d\u0433\u0430<\/p>\n<\/li>\n<li>\n<p>\u0421\u043e\u043a\u0440\u0430\u0449\u0435\u043d\u0438\u0435 \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u043d\u0430 \u043f\u043e\u0438\u0441\u043a \u043e\u0448\u0438\u0431\u043e\u043a<\/p>\n<\/li>\n<li>\n<p>\u0423\u043c\u0435\u043d\u044c\u0448\u0435\u043d\u0438\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0430 \u0434\u0435\u0444\u0435\u043a\u0442\u043e\u0432 \u0432 \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u0435\u043d\u0435<\/p>\n<\/li>\n<li>\n<p>\u0414\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f \u043e\u0436\u0438\u0434\u0430\u0435\u043c\u043e\u0433\u043e \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u044b<\/p>\n<\/li>\n<li>\n<p>\u0423\u043f\u0440\u043e\u0449\u0435\u043d\u0438\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0438 \u0438 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/p>\n<\/li>\n<li>\n<p>\u0411\u044b\u0441\u0442\u0440\u0430\u044f \u043e\u0431\u0440\u0430\u0442\u043d\u0430\u044f \u0441\u0432\u044f\u0437\u044c \u043e \u0440\u0430\u0431\u043e\u0442\u043e\u0441\u043f\u043e\u0441\u043e\u0431\u043d\u043e\u0441\u0442\u0438 \u0441\u0438\u0441\u0442\u0435\u043c\u044b<\/p>\n<\/li>\n<li>\n<p>\u041f\u043e\u0432\u044b\u0448\u0435\u043d\u0438\u0435 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0430 \u0440\u0430\u0437\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u043c\u043e\u0433\u043e \u043f\u0440\u043e\u0434\u0443\u043a\u0442\u0430<\/p>\n<\/li>\n<li>\n<p>\u041f\u043e\u0432\u044b\u0448\u0435\u043d\u0438\u0435 \u043d\u0430\u0434\u0435\u0436\u043d\u043e\u0441\u0442\u0438 \u0438 \u0441\u0442\u0430\u0431\u0438\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u0441\u0438\u0441\u0442\u0435\u043c\u044b<\/p>\n<\/li>\n<li>\n<p>\u041c\u043e\u0436\u0435\u0442\u0435 \u0441\u0430\u043c\u0438 \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u0442\u044c \u0432 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u044f\u0445, \u0435\u0441\u043b\u0438 \u044f \u0447\u0442\u043e-\u0442\u043e \u0437\u0430\u0431\u044b\u043b \ud83d\ude42<\/p>\n<\/li>\n<\/ol>\n<p>\u0410 \u0437\u0430 \u0441\u0447\u0451\u0442 \u0442\u043e\u0433\u043e, \u0447\u0442\u043e Spring \u043f\u0440\u0438\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f <a href=\"https:\/\/docs.spring.io\/spring-integration\/docs\/6.1.4\/reference\/html\/index-single.html#overview-components\">\u0441\u043b\u043e\u0435\u043d\u043e\u0439 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b<\/a>, \u0443 \u043d\u0430\u0441 \u043f\u043e\u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0432\u0435\u043b\u0438\u043a\u043e\u043b\u0435\u043f\u043d\u0430\u044f \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043a\u0430\u0436\u0434\u044b\u0439 \u0441\u043b\u043e\u0439 \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438. \u0421\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u0438 \u0438 \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0438 \u043c\u043e\u0433\u0443\u0442 \u043f\u043e\u043c\u043e\u0447\u044c \u0432 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u0438 \u044e\u043d\u0438\u0442 \u0442\u0435\u0441\u0442\u043e\u0432 \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0438\u0437 \u0443\u0440\u043e\u0432\u043d\u0435\u0439:<\/p>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/ad5\/3b4\/e83\/ad53b4e830c76aa7f95b609d7f0d1939.png\" width=\"1600\" height=\"1600\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/ad5\/3b4\/e83\/ad53b4e830c76aa7f95b609d7f0d1939.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/ad5\/3b4\/e83\/ad53b4e830c76aa7f95b609d7f0d1939.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<p>\u041d\u043e \u0434\u0430\u0436\u0435 \u0435\u0441\u043b\u0438 \u043c\u044b \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c \u043a\u0430\u0436\u0434\u044b\u0439 \u0443\u0440\u043e\u0432\u0435\u043d\u044c \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438, \u043c\u044b \u043d\u0435 \u043c\u043e\u0436\u0435\u043c \u0431\u044b\u0442\u044c \u0443\u0432\u0435\u0440\u0435\u043d\u043d\u044b\u043c\u0438 \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e \u0432\u043c\u0435\u0441\u0442\u0435 \u044d\u0442\u0438 \u0443\u0440\u043e\u0432\u043d\u0438 \u0442\u043e\u0436\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0442 \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e.<\/p>\n<p>\u0420\u0435\u0448\u0438\u0442\u044c \u044d\u0442\u0443 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443 \u043f\u043e\u043c\u043e\u0433\u0430\u044e\u0442 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0435 \u0442\u0435\u0441\u0442\u044b, \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u044e\u0449\u0438\u0435 \u0440\u0430\u0431\u043e\u0442\u0443 \u0432\u0441\u0435\u0445 \u0443\u0440\u043e\u0432\u043d\u0435\u0439 \u0432\u043c\u0435\u0441\u0442\u0435, \u0430 \u0442\u0430\u043a\u0436\u0435 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u0441 \u0432\u043d\u0435\u0448\u043d\u0438\u043c\u0438 \u0441\u0435\u0440\u0432\u0438\u0441\u0430\u043c\u0438, \u0441\u0438\u0441\u0442\u0435\u043c\u0430\u043c\u0438 \u0438 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0430\u043c\u0438.<\/p>\n<h3>\u041e\u0431\u0437\u043e\u0440 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0438 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0430 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439<\/h3>\n<p>\u0421\u0435\u0433\u043e\u0434\u043d\u044f \u043c\u044b \u0432\u043e\u0437\u044c\u043c\u0451\u043c \u043d\u0435\u0431\u0435\u0437\u044b\u0437\u0432\u0435\u0441\u0442\u043d\u044b\u0439 \u043f\u0440\u043e\u0435\u043a\u0442 <a href=\"https:\/\/github.com\/Amplicode\/amplicode-tutorials\/tree\/main\/integration-testing-docker-compose\">spring petclinic<\/a> \u0438 \u043d\u0430\u043f\u0438\u0448\u0435\u043c \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0435 \u0442\u0435\u0441\u0442\u044b \u0434\u043b\u044f CRUD REST \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u0430.<\/p>\n<p>\u041a\u0430\u043a \u0432\u0441\u0435\u0433\u0434\u0430, \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u0441\u043d\u0430\u0447\u0430\u043b\u0430 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c, \u0441 \u043a\u0430\u043a\u0438\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435\u043c \u043d\u0430\u043c \u043f\u0440\u0438\u0434\u0435\u0442\u0441\u044f \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c.<\/p>\n<p>\u0418\u0437 \u0441\u0442\u0430\u0440\u0442\u0435\u0440\u043e\u0432 \u0443 \u043d\u0430\u0441 \u043f\u0440\u0438\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u044e\u0442 Spring Web, Spring Data JPA, \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u0432\u0435\u0440\u0441\u0438\u043e\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445 \u0432\u044b\u0431\u0440\u0430\u043d Flyway, \u0442\u0430\u043a\u0436\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u044b \u0430\u043a\u0442\u0443\u0430\u0442\u043e\u0440\u044b \u0438 Kafka, \u0430 \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f PostgreSQL.<\/p>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/4cb\/a79\/c0d\/4cba79c0db4d231f4e6804d59912aa9c.png\" width=\"1121\" height=\"1202\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/4cb\/a79\/c0d\/4cba79c0db4d231f4e6804d59912aa9c.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/4cb\/a79\/c0d\/4cba79c0db4d231f4e6804d59912aa9c.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<p>\u0422\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043c\u044b \u0441\u0435\u0433\u043e\u0434\u043d\u044f \u0431\u0443\u0434\u0435\u043c <code>OwnerRestController<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435 \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u0447\u0443\u0442\u044c \u043f\u043e\u0437\u0436\u0435.<\/p>\n<p>\u041e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u0445\u043e\u0442\u0435\u043b \u0431\u044b \u0443\u0434\u0435\u043b\u0438\u0442\u044c \u0442\u043e\u043c\u0443, \u043a\u0430\u043a \u0443\u0441\u0442\u0440\u043e\u0435\u043d\u0430 \u0440\u0430\u0431\u043e\u0442\u0430 \u0441 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u0435\u043c \u043d\u0430 \u044d\u0442\u043e\u043c \u043f\u0440\u043e\u0435\u043a\u0442\u0435.<\/p>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/38b\/8e6\/41a\/38b8e641ad199a43baf39af961b8980a.png\" width=\"1120\" height=\"1342\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/38b\/8e6\/41a\/38b8e641ad199a43baf39af961b8980a.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/38b\/8e6\/41a\/38b8e641ad199a43baf39af961b8980a.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<p>\u0415\u0441\u0442\u044c \u0444\u0430\u0439\u043b services.yaml, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u043e\u0431\u044a\u044f\u0432\u043b\u0435\u043d\u044b \u0441\u0435\u0440\u0432\u0438\u0441\u044b Kafka \u0438 PostgreSQL, \u0430 \u0442\u0430\u043a\u0436\u0435 \u0435\u0441\u0442\u044c docker compose \u0444\u0430\u0439\u043b\u044b \u0434\u043b\u044f \u0434\u0435\u0432\u0435\u043b\u043e\u043f\u043c\u0435\u043d\u0442\u0430 \u0438 \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u0435\u043d\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0440\u0430\u0441\u0448\u0438\u0440\u044f\u044e\u0442 \u0441\u0435\u0440\u0432\u0438\u0441\u044b \u0438\u0437 \u0444\u0430\u0439\u043b\u0430 services.yaml. \u041f\u0440\u043e \u0442\u0430\u043a\u043e\u0439 \u0441\u043f\u043e\u0441\u043e\u0431 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f \u044f \u0443\u0436\u0435 \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u044b\u0432\u0430\u043b \u0432 \u0441\u0442\u0430\u0442\u044c\u0435 &#171;<a href=\"https:\/\/habr.com\/ru\/companies\/haulmont\/articles\/848696\/\">\u041b\u0443\u0447\u0448\u0438\u0439 \u0441\u043f\u043e\u0441\u043e\u0431 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u0439 \u0434\u043b\u044f Spring Boot \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e Docker Compose<\/a>&#171;.<\/p>\n<p>\u041f\u0435\u0440\u0435\u0439\u0434\u0435\u043c \u043a \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u0443.<\/p>\n<details class=\"spoiler\">\n<summary>OwnerRestController<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"java\">@RestController @RequestMapping(\"\/rest\/owners\") public class OwnerRestController {  \u00a0\u00a0\u00a0private final OwnerRepository ownerRepository; \u00a0\u00a0\u00a0private final OwnerMapper ownerMapper; \u00a0\u00a0\u00a0private final ObjectMapper objectMapper;  \u00a0\u00a0\u00a0public OwnerRestController(OwnerRepository ownerRepository, \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0OwnerMapper ownerMapper, \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0ObjectMapper objectMapper) { \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0this.ownerRepository = ownerRepository; \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0this.ownerMapper = ownerMapper; \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0this.objectMapper = objectMapper; \u00a0\u00a0\u00a0}  \u00a0\u00a0\u00a0@PostMapping \u00a0\u00a0\u00a0public OwnerDto create(@RequestBody @Valid OwnerDto ownerDto) { \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0if (ownerDto.getId() != null) { \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0throw new ResponseStatusException(HttpStatus.UNPROCESSABLE_ENTITY, \"Id must be null\"); \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0} \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Owner owner = ownerMapper.toEntity(ownerDto); \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Owner saved = ownerRepository.save(owner); \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0return ownerMapper.toOwnerDto(saved); \u00a0\u00a0\u00a0}  \u00a0\u00a0\u00a0@GetMapping(\"\/{id}\") \u00a0\u00a0\u00a0public OwnerMinimalDto getOne(@PathVariable Integer id) { \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Optional&lt;Owner&gt; ownerOptional = ownerRepository.findById(id); \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0return ownerMapper.toOwnerMinimalDto(ownerOptional.orElseThrow(() -&gt; \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0new ResponseStatusException(HttpStatus.NOT_FOUND, \"Entity with id %s not found\".formatted(id)))); \u00a0\u00a0\u00a0}  \u00a0\u00a0\u00a0@GetMapping \u00a0\u00a0\u00a0public PagedModel&lt;OwnerMinimalDto&gt; getAll(@ModelAttribute OwnerFilter ownerFilter, \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Pageable pageable) { \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Page&lt;Owner&gt; owners = ownerRepository.findAll(ownerFilter.toSpecification(), pageable); \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0var ownerDtoPage = owners.map(ownerMapper::toOwnerMinimalDto); \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0return new PagedModel&lt;&gt;(ownerDtoPage); \u00a0\u00a0\u00a0}   \u00a0\u00a0\u00a0@PutMapping(\"\/{id}\") \u00a0\u00a0\u00a0public OwnerDto update(@PathVariable Integer id, \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0@RequestBody @Valid OwnerDto dto) { \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0if (!dto.getId().equals(id)) { \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0throw new ResponseStatusException(HttpStatus.BAD_REQUEST, \"Id in request body and path variable must be equal\"); \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}  \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Owner owner = ownerRepository.findById(id) \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.orElseThrow(() -&gt; \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0new ResponseStatusException(HttpStatus.NOT_FOUND, \"Entity with id %s not found\".formatted(id)) \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0); \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0ownerMapper.updateWithNull(dto, owner); \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Owner resultOwner = ownerRepository.save(owner); \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0return ownerMapper.toOwnerDto(resultOwner); \u00a0\u00a0\u00a0}  \u00a0\u00a0\u00a0@PatchMapping(\"\/{id}\") \u00a0\u00a0\u00a0public OwnerDto patch(@PathVariable Integer id, \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0@RequestBody JsonNode patchNode) throws IOException { \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0if (patchNode.get(\"id\") == null || patchNode.get(\"id\").asInt() != id) { \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0throw new ResponseStatusException(HttpStatus.BAD_REQUEST, \"Id in request body and path variable must be equal\"); \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}  \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Owner owner = ownerRepository.findById(id) \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.orElseThrow(() -&gt; \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0new ResponseStatusException(HttpStatus.NOT_FOUND, \"Entity with id %s not found\".formatted(id)) \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0); \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0OwnerDto ownerDto = ownerMapper.toOwnerDto(owner);  \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0objectMapper.readerForUpdating(ownerDto) \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.readValue(patchNode); \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0ownerMapper.updateWithNull(ownerDto, owner); \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Owner resultOwner = ownerRepository.save(owner); \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0return ownerMapper.toOwnerDto(resultOwner); \u00a0\u00a0\u00a0}  \u00a0\u00a0\u00a0@DeleteMapping(\"\/{id}\") \u00a0\u00a0\u00a0public OwnerDto delete(@PathVariable Integer id) { \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Owner owner = ownerRepository.findById(id) \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.orElse(null); \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0if (owner != null) { \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0ownerRepository.delete(owner); \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0} \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0return ownerMapper.toOwnerDto(owner); \u00a0\u00a0\u00a0}  }<\/code><\/pre>\n<\/div>\n<\/details>\n<p>\u041d\u0430 \u0441\u0430\u043c\u043e\u043c \u0434\u0435\u043b\u0435 \u044d\u0442\u043e\u0442 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440 \u0431\u044b\u043b \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u043d \u0440\u0430\u043d\u0435\u0435 \u0434\u043b\u044f <a href=\"https:\/\/habr.com\/ru\/companies\/haulmont\/articles\/866060\/\">\u0441\u0442\u0430\u0442\u044c\u0438 &#171;\u0421\u043e\u0437\u0434\u0430\u0451\u043c CRUD REST API \u0432 Spring Boot \u0431\u044b\u0441\u0442\u0440\u043e \u0438 \u043f\u0440\u043e\u0441\u0442\u043e \u0432\u043c\u0435\u0441\u0442\u0435 \u0441 Amplicode&#187;<\/a>, \u0438 \u0435\u0433\u043e \u0440\u0430\u0431\u043e\u0442\u043e\u0441\u043f\u043e\u0441\u043e\u0431\u043d\u043e\u0441\u0442\u044c \u0431\u044b\u043b\u0430 \u043f\u0440\u043e\u0432\u0435\u0440\u0435\u043d\u0430 \u0443\u0436\u0435 \u0442\u043e\u0433\u0434\u0430 \u043f\u0440\u0438 \u043f\u043e\u043c\u043e\u0449\u0438 <a href=\"https:\/\/habr.com\/ru\/companies\/haulmont\/articles\/868106\/\">ConneKt (HTTP \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u043e\u0442 Amplicode)<\/a>. \u0422\u043e\u0433\u0434\u0430 \u0436\u0435 \u0431\u044b\u043b\u043e \u043f\u043e\u043a\u0430\u0437\u0430\u043d\u043e, \u043a\u0430\u043a \u0444\u043e\u0440\u043c\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043a \u044d\u0442\u043e\u043c\u0443 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u0443 \u0438 \u043f\u0438\u0441\u0430\u0442\u044c \u043a\u043e\u043c\u0430\u043d\u0434\u044b assert \u0434\u043b\u044f \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u043e\u0432.\u00a0<\/p>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/d1b\/05e\/439\/d1b05e439d0e122957e74ebd299d8235.png\" width=\"1596\" height=\"1600\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/d1b\/05e\/439\/d1b05e439d0e122957e74ebd299d8235.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/d1b\/05e\/439\/d1b05e439d0e122957e74ebd299d8235.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<p>\u041d\u043e \u044d\u0442\u043e \u0432\u0441\u0435 \u0436\u0435 \u0431\u044b\u043b\u043e \u0441\u043a\u043e\u0440\u0435\u0435 \u0440\u0443\u0447\u043d\u043e\u0435, \u0438\u043b\u0438, \u0442\u043e\u0447\u043d\u0435\u0435, \u043f\u043e\u043b\u0443\u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0435 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435. \u0421\u0430\u043c\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0438 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u043d\u0435\u0433\u043e \u043f\u043e\u0434\u043d\u0438\u043c\u0430\u043b\u0438\u0441\u044c \u0432\u0440\u0443\u0447\u043d\u0443\u044e. \u041e\u0434\u043d\u0430\u043a\u043e, \u043b\u044e\u0431\u043e\u043c\u0443 production-ready \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044e \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0435 \u0442\u0435\u0441\u0442\u044b. \u041f\u0440\u0438\u0441\u0442\u0443\u043f\u0438\u043c \u043a\u00a0 \u0438\u0445 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044e.\u00a0<\/p>\n<h4>\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u043e\u0432<\/h4>\n<p>\u041e\u0447\u0435\u0432\u0438\u0434\u043d\u043e, \u0447\u0442\u043e \u043c\u044b \u043d\u0435 \u0445\u043e\u0442\u0438\u043c \u0432\u0440\u0443\u0447\u043d\u0443\u044e \u043f\u043e\u0434\u043d\u0438\u043c\u0430\u0442\u044c \u0431\u0430\u0437\u0443 \u043a\u0430\u0436\u0434\u044b\u0439 \u0440\u0430\u0437, \u043a\u043e\u0433\u0434\u0430 \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0442\u0435\u0441\u0442\u044b. \u0425\u043e\u0442\u0435\u043b\u043e\u0441\u044c \u0431\u044b, \u0447\u0442\u043e\u0431\u044b \u0432\u0441\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u0441\u0435\u0440\u0432\u0438\u0441\u044b \u0441\u0442\u0430\u0440\u0442\u043e\u0432\u0430\u043b\u0438 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043a\u0430\u0436\u0434\u044b\u0439 \u0440\u0430\u0437, \u043a\u043e\u0433\u0434\u0430 \u044f \u0431\u0443\u0434\u0443 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c \u0442\u0435\u0441\u0442\u044b. \u0417\u0434\u0435\u0441\u044c \u0442\u043e\u0436\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u0434\u0445\u043e\u0434\u043e\u0432, \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <a href=\"https:\/\/testcontainers.com\/\">Testcontainers<\/a>, \u043d\u043e \u0441\u0435\u0433\u043e\u0434\u043d\u044f \u043c\u044b \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0435\u043c \u043f\u0440\u043e <a href=\"https:\/\/spring.io\/blog\/2023\/06\/21\/docker-compose-support-in-spring-boot-3-1\">Docker Compose Starter<\/a>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u044f\u0432\u0438\u043b\u0441\u044f \u043d\u0435 \u0442\u0430\u043a \u0434\u0430\u0432\u043d\u043e, \u0432 Spring Boot 3.1.<\/p>\n<p>\u041c\u044b \u043c\u043e\u0436\u0435\u043c \u0434\u043b\u044f \u043d\u0430\u0448\u0438\u0445 \u0442\u0435\u0441\u0442\u043e\u0432 \u0443\u043a\u0430\u0437\u0430\u0442\u044c docker compose \u0444\u0430\u0439\u043b, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0431\u0443\u0434\u0443\u0442 \u043e\u0431\u044a\u044f\u0432\u043b\u0435\u043d\u044b \u0432\u0441\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u043d\u0430\u043c \u0441\u0435\u0440\u0432\u0438\u0441\u044b, \u0438 \u0442\u043e\u0433\u0434\u0430 \u0432 \u043c\u043e\u043c\u0435\u043d\u0442 \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0432\u0441\u0435 \u044d\u0442\u0438 \u0441\u0435\u0440\u0432\u0438\u0441\u044b \u0431\u0443\u0434\u0443\u0442 \u043f\u043e\u0434\u043d\u0438\u043c\u0430\u0442\u044c\u0441\u044f \u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f.<\/p>\n<p>\u0412 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438 \u0443\u0436\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442 \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0441\u0435\u0440\u044c\u0435\u0437\u043d\u0430\u044f \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f docker compose \u0444\u0430\u0439\u043b\u043e\u0432, \u0438 \u044d\u0442\u0438\u043c \u0444\u0430\u043a\u0442\u043e\u043c \u0433\u0440\u0435\u0445 \u043d\u0435 \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f.<\/p>\n<p>\u041f\u0435\u0440\u0432\u044b\u043c \u0434\u0435\u043b\u043e\u043c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u043c \u0441\u0442\u0430\u0440\u0442\u0435\u0440. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043f\u0435\u0440\u0435\u0439\u0434\u0435\u043c \u0432 build.gradle \u0438 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u0443\u044e \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044c.<\/p>\n<pre><code class=\"kotlin\">dependencies { implementation ( \u00a0\u00a0\u00a0\u00a0'org.springframework.boot:spring-boot-docker-compose' ) }<\/code><\/pre>\n<p>\u0418 \u0442\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043e\u0442\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044e \u0441 docker compose, \u043a\u0430\u043a \u0431\u044b \u0441\u0442\u0440\u0430\u043d\u043d\u043e \u044d\u0442\u043e \u043d\u0438 \u0437\u0432\u0443\u0447\u0430\u043b\u043e. \u0412 \u043f\u0440\u043e\u0442\u0438\u0432\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0431\u044b\u043b\u043e \u0431\u044b \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0442\u044c \u0435\u0435 \u0434\u043b\u044f \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0445 \u043f\u0440\u043e\u0444\u0438\u043b\u0435\u0439 \u2014 \u043f\u0440\u043e\u0434, \u0434\u0435\u0432, \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c, \u043a\u0430\u043a\u043e\u0439 \u0444\u0430\u0439\u043b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0434\u043b\u044f \u043a\u0430\u043a\u043e\u0433\u043e \u043f\u0440\u043e\u0444\u0438\u043b\u044f, \u0447\u0442\u043e \u0432\u044b\u0445\u043e\u0434\u0438\u0442 \u0437\u0430 \u0440\u0430\u043c\u043a\u0438 \u0434\u0430\u043d\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0438.<\/p>\n<p>\u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u043f\u0435\u0440\u0435\u0439\u0434\u0435\u043c \u0432 <a href=\"http:\/\/application.properties\">application.properties<\/a> \u0438 \u043d\u0430\u043f\u0438\u0448\u0435\u043c \u0442\u0430\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0435:<\/p>\n<pre><code class=\"powershell\">spring.docker.compose.enabled=false<\/code><\/pre>\n<p>\u0414\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043d\u0430\u0448\u0435\u0433\u043e \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u0430 \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043a\u043b\u0430\u0441\u0441 <code>OwnerRestControllerTest<\/code> \u0438 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u0438:<\/p>\n<pre><code class=\"java\">@SpringBootTest @TestPropertySource(properties = { \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"spring.docker.compose.enabled=true\", \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"spring.docker.compose.skip.in-tests=false\", \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"spring.docker.compose.stop.command=down\", \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"spring.docker.compose.file=docker-compose-tests.yaml\" }) @AutoConfigureMockMvc public class OwnerRestControllerTest {     ... } <\/code><\/pre>\n<p>\u0417\u0430\u043e\u0441\u0442\u0440\u044f\u0442\u044c \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043d\u0430 \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0442\u0440\u0438\u0432\u0438\u0430\u043b\u044c\u043d\u044b\u0445 \u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u044f\u0445 <code>@SpringBootTest<\/code> \u0438 <code>@AutoConfigureMockMvc<\/code> \u044f \u043d\u0435 \u0431\u0443\u0434\u0443. \u0420\u0430\u0441\u0441\u043a\u0430\u0436\u0443 \u043f\u043e\u043f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u0440\u043e <code>@TestPropertySource<\/code>.<\/p>\n<p>\u0417\u0434\u0435\u0441\u044c \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u043e <code>spring.docker.compose.enabled<\/code> \u0440\u0430\u0437\u0440\u0435\u0448\u0430\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 docker compose \u0444\u0430\u0439\u043b\u043e\u0432 \u0432 \u043c\u043e\u043c\u0435\u043d\u0442 \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0442\u0435\u0441\u0442\u043e\u0432. \u0412\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u044d\u0442\u0443 \u0444\u0438\u0447\u0443 \u044f\u0432\u043d\u043e \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043f\u043e\u0442\u043e\u043c\u0443, \u0447\u0442\u043e \u043c\u044b \u0435\u0451 \u0440\u0430\u043d\u0435\u0435 \u0432\u044b\u043a\u043b\u044e\u0447\u0438\u043b\u0438 \u0432 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u043c \u0444\u0430\u0439\u043b\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f <code>application.properties<\/code>.<\/p>\n<p>\u0421\u0432\u043e\u0439\u0441\u0442\u0432\u043e <code>stop.command<\/code> \u0432\u044b\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0432 <code>down<\/code> \u0432\u043c\u0435\u0441\u0442\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e <code>scope<\/code> \u0432\u043e \u0438\u0437\u0431\u0435\u0436\u0430\u043d\u0438\u0435 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u043c\u0435\u0436\u0434\u0443 \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u043c\u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u043c\u0438 \u0442\u0435\u0441\u0442\u043e\u0432, \u0431\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u044f \u044d\u0442\u043e\u043c\u0443 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0443 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440 \u043f\u043e\u0441\u043b\u0435 \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0432\u0441\u0435\u0445 \u0442\u0435\u0441\u0442\u043e\u0432 \u0431\u0443\u0434\u0435\u0442 \u0443\u043d\u0438\u0447\u0442\u043e\u0436\u0430\u0442\u044c\u0441\u044f, \u0438 \u0431\u0443\u0434\u0435\u0442 \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c\u0441\u044f \u043d\u043e\u0432\u044b\u0439.\u00a0<\/p>\n<p>\u041f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u043c \u0448\u0430\u0433\u043e\u043c \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0444\u0430\u0439\u043b\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0434\u043b\u044f \u043d\u0430\u0448\u0438\u0445 \u0442\u0435\u0441\u0442\u043e\u0432: <code>docker-compose-tests.yaml<\/code>.\u00a0<\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u043c \u043e\u0441\u0442\u0430\u0435\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u044d\u0442\u043e\u0442 docker compose \u0444\u0430\u0439\u043b.<\/p>\n<details class=\"spoiler\">\n<summary>docker-compose-tests.yaml \u0444\u0430\u0439\u043b<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"yaml\">services: \u00a0\u00a0postgres-test: \u00a0\u00a0\u00a0\u00a0extends: \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0service: postgres \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0file: services.yaml \u00a0\u00a0\u00a0\u00a0ports: \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0- \"5432:5432\" \u00a0\u00a0\u00a0\u00a0environment: \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0POSTGRES_PASSWORD: postgres \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0POSTGRES_DB: postgres \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0POSTGRES_USER: postgres  \u00a0\u00a0kafka-test: \u00a0\u00a0\u00a0\u00a0extends: \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0service: kafka \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0file: services.yaml \u00a0\u00a0\u00a0\u00a0ports: \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0- \"9094:9094\" \u00a0\u00a0\u00a0\u00a0environment: \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0KAFKA_ADVERTISED_LISTENERS: PLAINTEXT:\/\/kafka-test:29094,PLAINTEXT_HOST:\/\/localhost:9094 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0KAFKA_LISTENERS: PLAINTEXT:\/\/kafka-test:29094,PLAINTEXT_HOST:\/\/0.0.0.0:9094,CONTROLLER:\/\/kafka-test:9093 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0KAFKA_CONTROLLER_QUORUM_VOTERS: 1@kafka-test:9093 \u00a0\u00a0\u00a0\u00a0healthcheck: \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0test: kafka-topics --bootstrap-server localhost:9094 --list \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0interval: 10s \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0timeout: 5s \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0start_period: 30s \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0retries: 5<\/code><\/pre>\n<\/div>\n<\/details>\n<p>\u041e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u0435 \u0433\u043e\u0442\u043e\u0432\u043e. \u041d\u0430\u043a\u043e\u043d\u0435\u0446-\u0442\u043e \u043c\u043e\u0436\u0435\u043c \u043f\u0440\u0438\u0441\u0442\u0443\u043f\u0438\u0442\u044c \u043d\u0435\u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u043a \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044e \u0442\u0435\u0441\u0442\u043e\u0432.<\/p>\n<h3>\u0422\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c POST-\u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442<\/h3>\n<pre><code class=\"java\">@PostMapping public OwnerDto create(@RequestBody @Valid OwnerDto ownerDto) { \u00a0\u00a0\u00a0if (ownerDto.getId() != null) { \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0throw new ResponseStatusException(HttpStatus.UNPROCESSABLE_ENTITY, \"Id must be null\"); \u00a0\u00a0\u00a0}  \u00a0\u00a0\u00a0Owner owner = ownerMapper.toEntity(ownerDto); \u00a0\u00a0\u00a0Owner saved = ownerRepository.save(owner); \u00a0\u00a0\u00a0return ownerMapper.toOwnerDto(saved); }<\/code><\/pre>\n<p>\u041c\u0435\u0442\u043e\u0434 \u0434\u043b\u044f POST\u2019\u0430 \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0442\u0440\u0438\u0432\u0438\u0430\u043b\u0435\u043d, \u043e\u0436\u0438\u0434\u0430\u0435\u043c \u043d\u0430 \u0432\u0445\u043e\u0434 DTO, \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c, \u0447\u0442\u043e \u043f\u043e\u043b\u0435 <code>id<\/code> \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 <code>null<\/code>, \u0430 \u0435\u0441\u043b\u0438 \u043d\u0435 \u0442\u0430\u043a, \u0442\u043e \u0432\u044b\u0431\u0440\u0430\u0441\u044b\u0432\u0430\u0435\u043c \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435, \u0442\u0430\u043a \u043a\u0430\u043a \u0430\u0439\u0434\u0438\u0448\u043d\u0438\u043a\u0438 \u0432 \u043d\u0430\u0448\u0435\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u044f\u043c \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u043d\u0430\u0437\u043d\u0430\u0447\u0430\u0442\u044c \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u043e, \u0430 \u0442\u043e\u0447\u043d\u0435\u0435 \u044d\u0442\u0438\u043c \u0431\u0443\u0434\u0435\u0442 \u0437\u0430\u043d\u0438\u043c\u0430\u0442\u044c\u0441\u044f \u0411\u0414. \u0414\u0430\u043b\u0435\u0435 \u0432\u0441\u0451 \u043f\u0440\u043e\u0441\u0442\u043e \u2014 \u043c\u0430\u043f\u0438\u043c DTO \u0432 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u044c, \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c \u0438 \u043c\u0430\u043f\u0438\u043c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u043e\u0431\u0440\u0430\u0442\u043d\u043e \u0432 DTO, \u0447\u0442\u043e\u0431\u044b \u0432\u0435\u0440\u043d\u0443\u0442\u044c \u043e\u0442\u0432\u0435\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e.<\/p>\n<p>\u0414\u043b\u044f \u0443\u0441\u043a\u043e\u0440\u0435\u043d\u0438\u044f \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u043a\u043e\u0434\u0430 \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0441\u044c \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435\u043c &#171;Create MocvkMvc Test&#187; \u043e\u0442 Amplicode:<\/p>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/b04\/29b\/3d4\/b0429b3d456494aa680e7d84e8fb0a41.png\" width=\"1600\" height=\"708\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/b04\/29b\/3d4\/b0429b3d456494aa680e7d84e8fb0a41.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/b04\/29b\/3d4\/b0429b3d456494aa680e7d84e8fb0a41.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<p>\u0410 \u0432\u043e\u0442 \u0438 \u043d\u0430\u0448 \u043f\u0435\u0440\u0432\u044b\u0439 \u0442\u0435\u0441\u0442:<\/p>\n<pre><code class=\"java\">@Test public void create() throws Exception { String ownerDto = \"\"\" \u00a0\u00a0\u00a0\u00a0{ \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"id\": 0, \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"firstName\": \"\", \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"lastName\": \"\", \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"address\": \"\", \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"city\": \"\", \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"telephone\": \"\" \u00a0\u00a0\u00a0\u00a0}\"\"\";  mockMvc.perform(post(\"\/rest\/owners\") \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.content(ownerDto) \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.contentType(MediaType.APPLICATION_JSON)) \u00a0\u00a0\u00a0\u00a0.andExpect(status() .isOk()) \u00a0\u00a0\u00a0\u00a0.andDo(print()); }<\/code><\/pre>\n<p>\u0417\u0434\u0435\u0441\u044c \u0435\u0441\u0442\u044c \u043f\u0440\u0435\u0434\u0437\u0430\u043f\u043e\u043b\u043d\u0435\u043d\u043d\u043e\u0435 DTO \u0441\u043e \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043e\u0439 \u0438, \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e, \u0441\u0430\u043c\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0441 \u043f\u043e\u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c expect-\u043e\u043c, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u043e\u0436\u0438\u0434\u0430\u0435\u0442\u0441\u044f \u0432\u043e\u0437\u0432\u0440\u0430\u0442 \u0441\u0442\u0430\u0442\u0443\u0441\u0430 \u201cOK\u201d.<\/p>\n<p>\u041f\u0435\u0440\u0432\u043e\u0435, \u0447\u0442\u043e \u0437\u0434\u0435\u0441\u044c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u2014 \u044d\u0442\u043e \u0437\u0430\u043f\u043e\u043b\u043d\u0438\u0442\u044c DTO, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043b Amplicode. \u041e\u043d \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043b \u043f\u043e\u043b\u044f, \u043e\u0442\u0442\u0430\u043b\u043a\u0438\u0432\u0430\u044f\u0441\u044c \u043e\u0442 \u043c\u043e\u0434\u0435\u043b\u0438, \u043d\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0432 \u043d\u0438\u0445 \u043f\u043e\u043a\u0430 \u043d\u0435 \u043f\u0440\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u044b.<\/p>\n<p>\u0417\u0430\u043d\u0438\u043c\u0430\u0442\u044c\u0441\u044f \u044d\u0442\u0438\u043c \u0432\u0440\u0443\u0447\u043d\u0443\u044e? \u0412 2025-\u043c \u0433\u043e\u0434\u0443? \u041d\u0443, \u044d\u0442\u043e \u043f\u0440\u044f\u043c\u043e \u043c\u043e\u0432\u0435\u0442\u043e\u043d \u043a\u0430\u043a\u043e\u0439-\u0442\u043e! \u0421\u0435\u0439\u0447\u0430\u0441 \u0442\u0430\u043a \u043d\u0438\u043a\u0442\u043e \u043d\u0435 \u0434\u0435\u043b\u0430\u0435\u0442, \u0432\u0441\u0435 \u043e\u0442\u0434\u0430\u044e\u0442 \u0442\u0430\u043a\u0443\u044e \u0440\u0430\u0431\u043e\u0442\u0443 \u043d\u0430 \u043e\u0442\u043a\u0443\u043f LLM. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u044f \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0441\u044c \u043f\u043b\u0430\u0433\u0438\u043d\u043e\u043c <a href=\"https:\/\/yandex.cloud\/en\/docs\/code-assistant\">SourceCraft Code Assistant<\/a> \u043e\u0442 \u042f\u043d\u0434\u0435\u043a\u0441\u0430.<\/p>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/06c\/175\/994\/06c1759948f9228db6bca7fef7dafb78.png\" width=\"1356\" height=\"706\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/06c\/175\/994\/06c1759948f9228db6bca7fef7dafb78.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/06c\/175\/994\/06c1759948f9228db6bca7fef7dafb78.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<p>\u0417\u0430\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u043c\u0438 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0439 DTO \u043d\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u0441\u044f, \u043d\u043e \u043c\u043e\u0436\u043d\u043e \u0434\u0430\u0442\u044c LLM \u043d\u0430\u043f\u0443\u0442\u0441\u0442\u0432\u0438\u0435, \u043d\u0430\u043c\u0435\u043a \u043d\u0430 \u0442\u043e, \u043a\u0430\u043a\u043e\u0439 DTO \u043d\u0430\u043c \u043d\u0443\u0436\u0435\u043d. \u041f\u0440\u043e\u0441\u0442\u043e \u043d\u0430\u0447\u043d\u0435\u043c \u043f\u0438\u0441\u0430\u0442\u044c String johnDoeDto, \u0438 \u0432\u0443\u0430\u043b\u044f, \u0438\u0441\u043a\u0443\u0441\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 \u0438\u043d\u0442\u0435\u043b\u043b\u0435\u043a\u0442 \u0434\u043e\u0433\u0430\u0434\u0430\u043b\u0441\u044f \u0447\u0442\u043e \u0438\u043c\u0435\u043d\u043d\u043e \u043c\u044b \u0445\u043e\u0442\u0438\u043c.<\/p>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/9e0\/76a\/02d\/9e076a02dc79190fe6df446e6d421bab.png\" width=\"739\" height=\"641\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/9e0\/76a\/02d\/9e076a02dc79190fe6df446e6d421bab.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/9e0\/76a\/02d\/9e076a02dc79190fe6df446e6d421bab.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<p>\u041a\u043e\u043d\u0435\u0447\u043d\u043e, \u0437\u0430 \u0418\u0418 \u043d\u0443\u0436\u043d\u043e \u043f\u0440\u0438\u0441\u043c\u0430\u0442\u0440\u0438\u0432\u0430\u0442\u044c, \u043e\u043d \u043c\u043e\u0436\u0435\u0442 \u0434\u043e\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u043e\u0448\u0438\u0431\u043a\u0438 \u043f\u0440\u0438 \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 \u043a\u043e\u0434\u0430, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043e\u043f\u0435\u0447\u0430\u0442\u043a\u0438 \u0432 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044f\u0445 \u043f\u043e\u043b\u0435\u0439, \u043d\u043e \u0432 \u043f\u043e\u0434\u0430\u0432\u043b\u044f\u044e\u0449\u0435\u043c \u0431\u043e\u043b\u044c\u0448\u0438\u043d\u0441\u0442\u0432\u0435 \u0441\u043b\u0443\u0447\u0430\u0435\u0432 \u043e\u043d \u0432\u0441\u0435 \u0434\u0435\u043b\u0430\u0435\u0442, \u043a\u0430\u043a \u043d\u0430\u0434\u043e, \u0435\u0441\u043b\u0438 \u0434\u0430\u0442\u044c \u0435\u043c\u0443 \u0445\u043e\u0440\u043e\u0448\u0438\u0439 \u0440\u0435\u0444\u0435\u0440\u0435\u043d\u0441.\u00a0<\/p>\n<p>\u0422\u0430\u043a\u043e\u0439 \u0441\u043f\u043e\u0441\u043e\u0431 \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u0438 \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u0441\u043b\u0443\u0436\u0438\u043b \u043e\u0442\u043b\u0438\u0447\u043d\u044b\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u043e\u043c \u043a\u043e\u043c\u0431\u0438\u043d\u0430\u0446\u0438\u0438 \u0440\u0430\u0431\u043e\u0442\u044b \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0445 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432. \u041c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0438 \u0434\u0435\u0442\u0435\u0440\u043c\u0438\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u0443\u044e \u043b\u043e\u0433\u0438\u043a\u0443 \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 \u043e\u0442 Amplicode, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u0435\u043b\u0438\u043a\u043e\u043b\u0435\u043f\u043d\u043e \u0437\u043d\u0430\u0435\u0442, \u043a\u0430\u043a\u0430\u044f \u043c\u043e\u0434\u0435\u043b\u044c \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u0430, \u043a\u0430\u043a\u0438\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043f\u043e\u043b\u0435\u0439 \u0431\u0443\u0434\u0435\u0442 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043f\u043e\u0434\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0438 \u043a\u0430\u043a \u0432 \u0442\u043e\u0447\u043d\u043e\u0441\u0442\u0438 \u043d\u0430\u0437\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u043f\u043e\u043b\u044f. \u0417\u0430\u0442\u0435\u043c \u043c\u044b \u043e\u0442\u0434\u0430\u043b\u0438 \u044d\u0442\u043e LLM, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043f\u043e \u043f\u0440\u0438\u043c\u0435\u0440\u0443 \u0437\u0430\u043f\u043e\u043b\u043d\u0438\u043b\u0430 \u043d\u0430\u0448 DTO \u043d\u0443\u0436\u043d\u044b\u043c\u0438 \u0434\u0430\u043d\u043d\u044b\u043c\u0438.\u00a0<\/p>\n<p>\u041e\u0441\u0442\u0430\u043b\u043e\u0441\u044c \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0432\u044b\u0437\u043e\u0432\u044b \u043c\u0435\u0442\u043e\u0434\u0430 <code>andExpect()<\/code>, \u0432 \u0447\u0435\u043c \u043d\u0430\u043c \u0442\u043e\u0436\u0435 \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u043c\u043e\u0433\u0430\u0442\u044c LLM. \u0415\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0435, \u0432 \u0447\u0435\u043c LLM \u043d\u0430\u043c \u043d\u0435 \u0441\u043c\u043e\u0436\u0435\u0442 \u043f\u043e\u043c\u043e\u0447\u044c. \u0412 \u0438\u0442\u043e\u0433\u0435 \u043c\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 expect&#8217;\u044b \u0434\u043b\u044f \u043d\u0430\u0448\u0435\u0433\u043e POST-\u0437\u0430\u043f\u0440\u043e\u0441\u0430:<\/p>\n<pre><code class=\"java\">@Test public void create() throws Exception { String ownerDto = \"\"\" \u00a0\u00a0\u00a0\u00a0{ \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"firstName\": \"John\", \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"lastName\": \"Doe\", \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"address\": \"123 Main St\", \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"city\": \"Anytown\", \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"telephone\": \"5555555555\" \u00a0\u00a0\u00a0\u00a0} \u00a0\u00a0\u00a0\u00a0\"\"\"; mockMvc.perform(post(\"\/rest\/owners\") \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.content(ownerDto) \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.contentType(MediaType.APPLICATION_JSON)) \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.andExpect(status().isOk())     .andExpect(jsonPath(\"$.firstName\").value(\"John\"))     .andExpect(jsonPath(\"$.lastName\").value(\"Doe\"))     .andExpect(jsonPath(\"$.address\").value(\"123 Main St\"))     .andExpect(jsonPath(\"$.city\").value(\"Anytown\"))     .andExpect(jsonPath(\"$.telephone\").value(\"5555555555\"))     .andExpect(jsonPath(\"$.id\").isNumber())     .andDo(print()); }<\/code><\/pre>\n<p>\u0418\u0442\u0430\u043a, \u043c\u044b \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u043b\u0438 \u0437\u0430\u043f\u0440\u043e\u0441 \u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u043b\u0438 \u043e\u0442\u0432\u0435\u0442. \u041e\u0434\u043d\u0430\u043a\u043e, \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0435 \u0442\u0435\u0441\u0442\u044b \u0442\u0430\u043a\u0436\u0435 \u0434\u043e\u043b\u0436\u043d\u044b \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c \u0442\u043e, \u0441 \u0447\u0435\u043c \u043c\u044b \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0443\u0435\u043c. \u0412 \u0434\u0430\u043d\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043c\u044b \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0443\u0435\u043c \u0441 \u0431\u0430\u0437\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u043d\u0430\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0443\u0431\u0435\u0434\u0438\u0442\u044c\u0441\u044f, \u0447\u0442\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0432 \u044d\u0442\u043e\u0439 \u0431\u0430\u0437\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u043b\u043e\u0441\u044c. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u043c \u0447\u0442\u043e \u0438\u043c\u0435\u043d\u043d\u043e \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u043b\u043e\u0441\u044c \u0432 \u0431\u0430\u0437\u0435:<\/p>\n<pre><code class=\"java\">@Test public void create() throws Exception { String ownerDto = \"\"\" \u00a0\u00a0\u00a0\u00a0{ \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"firstName\": \"John\", \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"lastName\": \"Doe\", \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"address\": \"123 Main St\", \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"city\": \"Anytown\", \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"telephone\": \"5555555555\" \u00a0\u00a0\u00a0\u00a0} \u00a0\u00a0\u00a0\u00a0\"\"\";  mockMvc.perform(post(\"\/rest\/owners\") \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.content(ownerDto) \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.contentType(MediaType.APPLICATION_JSON)) \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.andExpect(status().isOk())     .andExpect(jsonPath(\"$.firstName\").value(\"John\"))     .andExpect(jsonPath(\"$.lastName\").value(\"Doe\"))     .andExpect(jsonPath(\"$.address\").value(\"123 Main St\"))     .andExpect(jsonPath(\"$.city\").value(\"Anytown\"))     .andExpect(jsonPath(\"$.telephone\").value(\"5555555555\"))     .andExpect(jsonPath(\"$.id\").isNumber())     .andReturn();          String contentAsString = mvcResult.getResponse()     .getContentAsString();     Integer id = JsonPath.parse(contentAsString)     .read(\"$.id\");          Owner owner = ownerRepository.findById(id)     .orElseThrow();          assertThat(owner.getFirstName()).isEqualTo(\"John\");     assertThat(owner.getLastName()).isEqualTo(\"Doe\");     assertThat(owner.getAddress()).isEqualTo(\"123 Main St\");     assertThat(owner.getCity()).isEqualTo(\"Anytown\");     assertThat(owner.getTelephone()).isEqualTo(\"5555555555\");     ownerRepository.delete(owner); }<\/code><\/pre>\n<p>\u0421\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u0442\u0435\u0441\u0442 \u0442\u043e\u0436\u0435 \u0431\u0443\u0434\u0435\u0442 \u0434\u043b\u044f \u043c\u0435\u0442\u043e\u0434\u0430 POST, \u0442\u0430\u043a \u043a\u0430\u043a \u0443 \u043d\u0430\u0441 \u0438\u043c\u0435\u0435\u0442\u0441\u044f \u0432\u0442\u043e\u0440\u043e\u0435 \u0432\u0435\u0442\u0432\u043b\u0435\u043d\u0438\u0435, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u043c\u044b \u0435\u0449\u0435 \u043d\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u043b\u0438. \u042d\u0442\u043e \u0442\u0430 \u0441\u0438\u0442\u0443\u0430\u0446\u0438\u044f, \u043a\u043e\u0433\u0434\u0430 \u043c\u044b \u043f\u0435\u0440\u0435\u0434\u0430\u0435\u043c DTO \u0441 id, \u0438 \u0432 \u044d\u0442\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043c\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u0432\u044b\u0431\u0440\u043e\u0441\u0438\u0442\u044c \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435.\u00a0<\/p>\n<p>\u0410\u043d\u0430\u043b\u043e\u0433\u0438\u0447\u043d\u044b\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u043c \u0442\u0435\u0441\u0442 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e Amplicode \u0438 \u0434\u043e\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u043c \u043f\u043e\u0434 \u043d\u0430\u0448\u0438 \u043d\u0443\u0436\u0434\u044b:<\/p>\n<pre><code class=\"java\">@Test public void createIdNotNull() throws Exception { String ownerDto = \"\"\" \u00a0\u00a0\u00a0\u00a0{ \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"id\": 1, \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"firstName\": \"John\", \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"lastName\": \"Doe\", \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"address\": \"123 Main St\", \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"city\": \"Anytown\", \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"telephone\": \"5555555555\" \u00a0\u00a0\u00a0\u00a0}\"\"\";  mockMvc.perform(post(\"\/rest\/owners\") \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.content(ownerDto) \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.contentType(MediaType.APPLICATION_JSON)) \u00a0\u00a0\u00a0\u00a0.andExpect(status() \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.isUnprocessableEntity());  int size = ownerRepository.findAll() \u00a0\u00a0\u00a0\u00a0.size();  assertThat(size).isEqualTo(0); }<\/code><\/pre>\n<p>\u0414\u043b\u044f \u0442\u0435\u043a\u0443\u0449\u0435\u0433\u043e \u0442\u0435\u0441\u0442\u0430 \u0432\u0430\u0436\u043d\u043e \u0443\u0431\u0435\u0434\u0438\u0442\u044c\u0441\u044f \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e \u043c\u044b \u043d\u0435 \u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u043c \u043d\u0438\u0447\u0435\u0433\u043e \u043b\u0438\u0448\u043d\u0435\u0433\u043e \u0432 \u0431\u0430\u0437\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u0441\u043b\u0435 \u043f\u0440\u043e\u0433\u043e\u043d\u0430 \u0442\u0435\u0441\u0442\u0430. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u043c \u0432\u0441\u0435 \u0437\u0430\u043f\u0438\u0441\u0438 \u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u043c, \u0447\u0442\u043e <code>size<\/code> \u0440\u0430\u0432\u0435\u043d <code>0<\/code>.<\/p>\n<h3>\u0422\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c GET-\u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442<\/h3>\n<p>\u0421\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u043d\u0430\u043f\u0438\u0448\u0435\u043c \u0442\u0435\u0441\u0442 \u0434\u043b\u044f \u043c\u0435\u0442\u043e\u0434\u0430 GET.\u00a0<\/p>\n<pre><code class=\"java\">@GetMapping(\"\/{id}\") public OwnerMinimalDto getOne(@PathVariable Integer id) { \u00a0\u00a0\u00a0Optional&lt;Owner&gt; ownerOptional = ownerRepository.findById(id); \u00a0\u00a0\u00a0return ownerMapper.toOwnerMinimalDto(ownerOptional.orElseThrow(() -&gt; \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0new ResponseStatusException(HttpStatus.NOT_FOUND,                                         \"Entity with id %s not found\".formatted(id)))            ); } <\/code><\/pre>\n<p>\u0413\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u043c \u0437\u0430\u0433\u043e\u0442\u043e\u0432\u043a\u0443 \u0442\u0435\u0441\u0442\u0430, \u043a\u0430\u043a \u0438 \u0440\u0430\u043d\u044c\u0448\u0435, \u043d\u043e, \u0432 \u043e\u0442\u043b\u0438\u0447\u0438\u0438 \u043e\u0442 \u0442\u0435\u0441\u0442\u043e\u0432 \u0434\u043b\u044f \u043c\u0435\u0442\u043e\u0434\u0430 POST, \u0437\u0434\u0435\u0441\u044c \u043d\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u044f\u0442\u0441\u044f \u043a\u0430\u043a\u0438\u0435-\u0442\u043e \u0434\u0430\u043d\u043d\u044b\u0435 \u0432 \u0431\u0430\u0437\u0435, \u0441 \u043a\u043e\u0442\u043e\u0440\u044b\u043c\u0438 \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c. \u042d\u0442\u0438 \u0434\u0430\u043d\u043d\u044b\u0435 \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0437\u0430\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u0442\u044c \u0438\u0437 \u0442\u0435\u043b\u0430 \u0442\u0435\u0441\u0442\u0430 \u0447\u0435\u0440\u0435\u0437 \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442.\u00a0\u00a0<\/p>\n<p>\u0415\u0441\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u043f\u043e\u0441\u043e\u0431\u043e\u0432 \u0432\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435 \u0432 \u0431\u0430\u0437\u0443:\u00a0<\/p>\n<ul>\n<li>\n<p>\u043d\u0430 \u044d\u0442\u0430\u043f\u0435 \u043f\u043e\u0434\u043d\u044f\u0442\u0438\u044f \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0430, \u0443\u043a\u0430\u0437\u0430\u0432 \u0438\u043d\u0438\u0442 \u0441\u043a\u0440\u0438\u043f\u0442\u044b \u0432 \u0444\u0430\u0439\u043b\u0435 docker compose<\/p>\n<\/li>\n<li>\n<p>\u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f Spring Data JPA \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0435\u043c \u0438 \u0432\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435 \u0432 \u0442\u0435\u0441\u0442\u0435 \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e\u00a0<\/p>\n<\/li>\n<li>\n<p>\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u044e <code>@Sql<\/code>.\u00a0\u00a0<\/p>\n<\/li>\n<\/ul>\n<p>\u0414\u043b\u044f \u0446\u0435\u043b\u0435\u0439 \u0434\u0430\u043d\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0438 \u043c\u044b \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u0441\u044f \u0442\u0440\u0435\u0442\u044c\u0438\u043c \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u043e\u043c.\u00a0<\/p>\n<p><em>\u041d\u0430\u043f\u0438\u0448\u0438\u0442\u0435 \u0432 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u044f\u0445, \u043a\u0430\u043a\u0438\u043c \u0441\u043f\u043e\u0441\u043e\u0431\u043e\u043c \u043f\u0440\u0435\u0434\u043f\u043e\u0447\u0438\u0442\u0430\u0435\u0442\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043b\u0438\u0447\u043d\u043e \u0432\u044b!<\/em><\/p>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u043d\u0430\u0434 \u0442\u0435\u0441\u0442\u043e\u043c \u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u044e <code>@Sql<\/code> \u0438 \u0443\u043a\u0430\u0436\u0435\u043c \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0441\u043a\u0440\u0438\u043f\u0442\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f, \u0430 \u0442\u0430\u043a\u0436\u0435 <code>executionPhase<\/code>:<\/p>\n<p><code>@Sql(scripts = \"classpath:insert-owners.sql\", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)<\/code><\/p>\n<p>\u0423\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0439 \u0437\u0434\u0435\u0441\u044c \u0441\u043a\u0440\u0438\u043f\u0442, <code>insert-owners.sql<\/code>, \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c\u0441\u044f \u0434\u043e \u043d\u0430\u0448\u0435\u0433\u043e \u0442\u0435\u0441\u0442\u043e\u0432\u043e\u0433\u043e \u043c\u0435\u0442\u043e\u0434\u0430 \u0438 \u0438\u043c\u0435\u043d\u043d\u043e \u0432 \u043d\u0435\u043c \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0432\u0441\u0442\u0430\u0432\u043b\u044f\u0442\u044c \u0432 \u0431\u0430\u0437\u0443 \u043d\u043e\u0432\u044b\u0435 \u0437\u0430\u043f\u0438\u0441\u0438. \u0422\u0430\u043a\u0436\u0435 \u043d\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u0441\u043a\u0440\u0438\u043f\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u043d\u0430\u0437\u044b\u0432\u0430\u0442\u044c\u0441\u044f <code>delete-owners.sql<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c\u0441\u044f \u043f\u043e\u0441\u043b\u0435 \u043d\u0430\u0448\u0435\u0433\u043e \u0442\u0435\u0441\u0442\u0430 \u0438 \u0431\u0443\u0434\u0435\u0442 \u0443\u0434\u0430\u043b\u044f\u0442\u044c \u0432\u0441\u0435 \u0442\u0435 \u0434\u0430\u043d\u043d\u044b\u0435, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u044b \u0432\u0441\u0442\u0430\u0432\u0438\u043b\u0438.<\/p>\n<p><code>@Sql(scripts = \"classpath:delete-owners.sql\", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)<\/code><\/p>\n<p>\u0423\u0434\u0430\u043b\u044f\u0435\u043c \u043c\u044b \u0434\u0430\u043d\u043d\u044b\u0435 \u0434\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0442\u0435\u0441\u0442\u044b \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e \u0438\u0437\u043e\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u043c\u0438 \u0434\u0440\u0443\u0433 \u043e\u0442 \u0434\u0440\u0443\u0433\u0430 \u0438 \u0447\u0442\u043e\u0431\u044b \u0434\u0430\u043d\u043d\u044b\u0435 \u043e\u0434\u043d\u043e\u0433\u043e \u0442\u0435\u0441\u0442\u0430 \u043d\u0435 \u043d\u0430\u043a\u043b\u0430\u0434\u044b\u0432\u0430\u043b\u0438\u0441\u044c \u043d\u0430 \u0434\u0440\u0443\u0433\u043e\u0439.\u00a0<\/p>\n<p>Amplicode \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043d\u0430\u043c \u043d\u0435\u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0439 \u043f\u043e\u043a\u0430 \u0447\u0442\u043e sql \u0444\u0430\u0439\u043b \u043f\u0440\u044f\u043c\u043e \u0438\u0437 \u043e\u043a\u043d\u0430 \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0430 \u043a\u043e\u0434\u0430.\u00a0<\/p>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/a3d\/532\/1a4\/a3d5321a466bc44c2b8252e35c1b1031.png\" width=\"1600\" height=\"734\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/a3d\/532\/1a4\/a3d5321a466bc44c2b8252e35c1b1031.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/a3d\/532\/1a4\/a3d5321a466bc44c2b8252e35c1b1031.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<p>\u0414\u0430\u043b\u0435\u0435 \u0432 \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u043e\u043c \u0444\u0430\u0439\u043b\u0435 \u0441\u043d\u043e\u0432\u0430 \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u0441\u044f \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044f\u043c\u0438 Amplicode, \u0447\u0442\u043e\u0431\u044b \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c insert \u0434\u043b\u044f \u0442\u0430\u0431\u043b\u0438\u0446\u044b owners.<\/p>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/336\/1de\/834\/3361de834368d0d7ca875f049d14d530.png\" width=\"1600\" height=\"1055\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/336\/1de\/834\/3361de834368d0d7ca875f049d14d530.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/336\/1de\/834\/3361de834368d0d7ca875f049d14d530.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<p>\u0412 \u0434\u0430\u043b\u044c\u043d\u0435\u0439\u0448\u0435\u043c, \u043a\u043e\u0433\u0434\u0430 \u0443 \u043d\u0430\u0441 \u043f\u043e\u044f\u0432\u0438\u0442\u0441\u044f \u043f\u0440\u0438\u043c\u0435\u0440 \u043a\u043e\u043c\u0430\u043d\u0434\u044b insert, \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c \u0435\u0433\u043e LLM, \u0447\u0442\u043e\u0431\u044b \u043e\u043d\u0430 \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043b\u0430 \u0435\u0449\u0435 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u0434\u043e\u0431\u043d\u044b\u0445 SQL-\u0432\u044b\u0440\u0430\u0436\u0435\u043d\u0438\u0439.\u00a0<\/p>\n<pre><code class=\"sql\">INSERT INTO owners (id, first_name, last_name, address, city, telephone) VALUES (1, 'John', 'Doe', '123 Main Street', 'New York', '5555555555');  INSERT INTO owners (id, first_name, last_name, address, city, telephone) VALUES (2, 'Jane', 'Doe', '456 Main Street', 'New York', '5555555556');  INSERT INTO owners (id, first_name, last_name, address, city, telephone) VALUES (3, 'Jim', 'Brown', '789 Main Street', 'New York', '5555555557');  INSERT INTO owners (id, first_name, last_name, address, city, telephone) VALUES (4, 'Jill', 'Will', '101 Main Street', 'New York', '5555555558');  INSERT INTO owners (id, first_name, last_name, address, city, telephone) VALUES (5, 'Jack', 'White', '123 Main Street', 'New York', '5555555559');<\/code><\/pre>\n<p>\u041e\u0441\u0442\u0430\u0435\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0444\u0430\u0439\u043b <code>delete-owners.sql<\/code> \u0438 \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c <code>delete<\/code> \u0432\u044b\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0434\u043b\u044f <code>owners<\/code> \u0432\u043a\u043b\u044e\u0447\u0438\u0432 \u0432 \u0441\u043f\u0438\u0441\u043e\u043a \u043d\u0430 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u0440\u043e\u0432\u043d\u043e \u0442\u0435 <code>id<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u044b \u0443\u043a\u0430\u0437\u0430\u043b\u0438 \u0432 \u0444\u0430\u0439\u043b\u0435 <code>insert-owners.sql<\/code>.\u00a0<\/p>\n<pre><code class=\"sql\">DELETE FROM owners WHERE id IN (1, 2, 3, 4, 5);<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u043f\u0438\u0448\u0435\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 expect\u2019\u044b \u0432\u043d\u0443\u0442\u0440\u0438 \u043d\u0430\u0448\u0435\u0433\u043e \u0442\u0435\u0441\u0442\u0430.<\/p>\n<pre><code class=\"java\">@Test @Sql(scripts = \"classpath:insert-owners.sql\",       executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) @Sql(scripts = \"classpath:delete-owners.sql\",       executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) public void getOne() throws Exception { mockMvc.perform(get(\"\/rest\/owners\/{0}\", \"1\")) \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.andExpect(status().isOk()) \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.andExpect(jsonPath(\"$.firstName\").value(\"John\")) \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.andExpect(jsonPath(\"$.lastName\").value(\"Doe\")) \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.andExpect(jsonPath(\"$.address\").value(\"123 Main Street\")) \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.andExpect(jsonPath(\"$.city\").value(\"New York\")) \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.andExpect(jsonPath(\"$.telephone\").value(\"5555555555\")) \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.andExpect(jsonPath(\"$.id\").value(1)); }<\/code><\/pre>\n<p>\u0421\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u0442\u0435\u0441\u0442 \u0442\u043e\u0436\u0435 \u0431\u0443\u0434\u0435\u0442 \u0434\u043b\u044f \u043c\u0435\u0442\u043e\u0434\u0430 GET, \u0434\u043b\u044f \u0442\u043e\u0433\u043e \u0436\u0435 \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u0430, \u043d\u043e \u0442\u0435\u043f\u0435\u0440\u044c \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u043c \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0439 \u201cNot Found\u201d.<\/p>\n<p>\u0422\u0435\u0441\u0442 \u0434\u043b\u044f \u043d\u0435\u043d\u0430\u0439\u0434\u0435\u043d\u043d\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438 \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0442\u0440\u0438\u0432\u0438\u0430\u043b\u0435\u043d. \u0414\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439 \u0442\u0435\u0441\u0442 \u0438 \u0437\u0430\u043c\u0435\u043d\u0438\u0442\u044c \u043e\u0436\u0438\u0434\u0430\u0435\u043c\u044b\u0439 \u0441\u0442\u0430\u0442\u0443\u0441 \u043d\u0430 NotFound.<\/p>\n<pre><code class=\"java\">@Test public void getOneNotFound() throws Exception { mockMvc.perform(get(\"\/rest\/owners\/{0}\", \"0\")) \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.andExpect(status().isNotFound()); }<\/code><\/pre>\n<h3>\u0422\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c GET-\u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442 \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u043c\u043d\u043e\u0433\u0438\u0445 \u0437\u0430\u043f\u0438\u0441\u0435\u0439<\/h3>\n<p>\u0421\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u043d\u0430 \u043e\u0447\u0435\u0440\u0435\u0434\u0438 \u0442\u0435\u0441\u0442 <code>getAll()<\/code>, \u0442\u0435\u0441\u0442\u0438\u0440\u0443\u044e\u0449\u0438\u0439 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u043c\u0435\u0442\u043e\u0434\u0430 GET, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u043e \u0437\u0430\u043f\u0438\u0441\u0435\u0439 \u043f\u043e <code>id<\/code>.\u00a0<\/p>\n<pre><code class=\"java\">@GetMapping public PagedModel&lt;OwnerMinimalDto&gt; getAll(@ModelAttribute OwnerFilter ownerFilter, \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Pageable pageable) { \u00a0\u00a0\u00a0Page&lt;Owner&gt; owners = ownerRepository.findAll(ownerFilter.toSpecification(), pageable); \u00a0\u00a0\u00a0var ownerDtoPage = owners.map(ownerMapper::toOwnerMinimalDto); \u00a0\u00a0\u00a0return new PagedModel&lt;&gt;(ownerDtoPage); }<\/code><\/pre>\n<p>\u0412 \u0446\u0435\u043b\u043e\u043c, \u0435\u0433\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0434\u043e\u043b\u0436\u043d\u0430 \u0437\u0430\u043a\u043b\u044e\u0447\u0430\u0442\u044c\u0441\u044f \u0432 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0435 \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e\u0439 \u0440\u0430\u0431\u043e\u0442\u044b \u0444\u0438\u043b\u044c\u0442\u0440\u043e\u0432 \u043f\u043e \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u0438 \u0432\u043c\u0435\u0441\u0442\u0435, \u043f\u0430\u0433\u0438\u043d\u0430\u0446\u0438\u0438 \u0438 \u0442\u0434. \u041d\u043e \u0432\u0441\u0435 \u043e\u043d\u0438 \u0431\u0443\u0434\u0443\u0442 \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0442\u0440\u0438\u0432\u0438\u0430\u043b\u044c\u043d\u044b\u0435, \u0441\u0438\u043b\u044c\u043d\u043e \u043f\u043e\u0445\u043e\u0436\u0438\u0435 \u0434\u0440\u0443\u0433 \u043d\u0430 \u0434\u0440\u0443\u0433\u0430. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u043d\u0430\u043f\u0438\u0448\u0435\u043c \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u0438\u043d, \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0435 \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u043e, \u0435\u0441\u043b\u0438 \u0431\u0443\u0434\u0435\u0442 \u0436\u0435\u043b\u0430\u043d\u0438\u0435 \u043f\u043e\u043f\u0440\u0430\u043a\u0442\u0438\u043a\u043e\u0432\u0430\u0442\u044c\u0441\u044f. \u041d\u0430\u043f\u043e\u043c\u0438\u043d\u0430\u044e, \u0447\u0442\u043e \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043f\u0440\u043e\u0435\u043a\u0442 \u043e\u043f\u0443\u0431\u043b\u0438\u043a\u043e\u0432\u0430\u043d \u043d\u0430 <a href=\"https:\/\/github.com\/Amplicode\/amplicode-tutorials\/tree\/main\/integration-testing-docker-compose\">GitHub<\/a>.<\/p>\n<p>\u0421\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u043c \u0442\u0435\u0441\u0442, \u0432\u0441\u0442\u0430\u0432\u0438\u043c \u0434\u0430\u043d\u043d\u044b\u0435, \u043a\u0430\u043a \u043c\u044b \u044d\u0442\u043e \u0434\u0435\u043b\u0430\u043b\u0438 \u0440\u0430\u043d\u0435\u0435, \u043f\u043e\u0441\u043b\u0435 \u0447\u0435\u0433\u043e \u0443\u0434\u0430\u043b\u0438\u043c \u043b\u0438\u0448\u043d\u0438\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438, \u043e\u0441\u0442\u0430\u0432\u0438\u0432 \u0442\u043e\u043b\u044c\u043a\u043e <code>LastNameContains<\/code>.<\/p>\n<p>\u0411\u0443\u0434\u0435\u043c \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c \u0442\u043e\u0442 \u0444\u0430\u043a\u0442, \u0447\u0442\u043e \u0441\u0440\u0435\u0434\u0438 \u0444\u0430\u043c\u0438\u043b\u0438\u0439 \u0438\u043c\u0435\u0435\u0442\u0441\u044f \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 <code>Doe<\/code>. \u041c\u044b \u043e\u0441\u0442\u0430\u0432\u0438\u043b\u0438 \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u0432\u0435 \u0437\u0430\u043f\u0438\u0441\u0438 \u0441 \u0442\u0430\u043a\u043e\u0439 \u0444\u0430\u043c\u0438\u043b\u0438\u0435\u0439, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0432 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u043e\u043c DTO \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u0438\u043c\u0435\u043d\u043d\u043e \u0434\u0432\u0430 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430. \u0422\u0435\u0441\u0442 \u043f\u0440\u0438\u043c\u0435\u0442 \u0442\u0430\u043a\u043e\u0439 \u0432\u0438\u0434, \u0438 \u044d\u0442\u043e\u0442 \u0432\u0438\u0434 \u0431\u0443\u0434\u0435\u0442 \u043e\u043a\u043e\u043d\u0447\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u043c:<\/p>\n<pre><code class=\"java\">@Test @Sql(scripts = \"classpath:insert-owners.sql\", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) @Sql(scripts = \"classpath:delete-owners.sql\", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) public void getAll() throws Exception { mockMvc.perform(get(\"\/rest\/owners\") \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.param(\"lastNameContains\", \"Doe\")) \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.andExpect(status().isOk()) \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.andExpect(jsonPath(\"$.content[*].lastName\").value(everyItem(is(\"Doe\")))) \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.andExpect(jsonPath(\"$.content.length()\").value(2)); }<\/code><\/pre>\n<h3>\u0422\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c PATCH-\u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442<\/h3>\n<p>\u041d\u0430\u043a\u043e\u043d\u0435\u0446, \u043f\u0435\u0440\u0435\u0439\u0434\u0435\u043c \u043a \u0441\u0430\u043c\u044b\u043c \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u043c \u043c\u0435\u0442\u043e\u0434\u0430\u043c \u2014 PUT \u0438 PATCH. \u0418\u0445 \u0435\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u0430\u044f \u0440\u0430\u0437\u043d\u0438\u0446\u0430 \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e PUT \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u0442 \u0437\u0430\u043f\u0438\u0441\u044c \u0446\u0435\u043b\u0438\u043a\u043e\u043c, \u0430 PATCH \u043b\u0438\u0448\u044c \u0447\u0430\u0441\u0442\u0438\u0447\u043d\u043e. \u041f\u0440\u0438 \u044d\u0442\u043e\u043c \u043c\u0435\u0442\u043e\u0434 PATCH \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0434\u0430\u0436\u0435 \u0434\u0435\u043b\u0430\u0442\u044c \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0440\u0430\u0432\u043d\u044b\u043c\u0438 <code>null<\/code>. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u043c\u044b \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u043c \u0438\u043c\u0435\u043d\u043d\u043e \u0435\u0433\u043e. PUT \u043c\u043e\u0436\u043d\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u043f\u043e \u0430\u043d\u0430\u043b\u043e\u0433\u0438\u0438, \u0442\u0430\u043a \u043a\u0430\u043a \u043e\u043d \u043f\u0440\u043e\u0449\u0435 \u0438 \u0438\u043c\u0435\u0435\u0442 \u043c\u0435\u043d\u044c\u0448\u0435 corner case\u2019\u043e\u0432.<\/p>\n<pre><code class=\"java\">@PatchMapping(\"\/{id}\") public OwnerDto patch(@PathVariable Integer id, \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0@RequestBody JsonNode patchNode) throws IOException { \u00a0\u00a0\u00a0if (patchNode.get(\"id\") == null || patchNode.get(\"id\").asInt() != id) { \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0throw new ResponseStatusException(HttpStatus.BAD_REQUEST, \"Id in request body and path variable must be equal\"); \u00a0\u00a0\u00a0}  \u00a0\u00a0\u00a0Owner owner = ownerRepository.findById(id) \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.orElseThrow(() -&gt; \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0new ResponseStatusException(HttpStatus.NOT_FOUND, \"Entity with id %s not found\".formatted(id)) \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0);  \u00a0\u00a0\u00a0OwnerDto ownerDto = ownerMapper.toOwnerDto(owner); \u00a0\u00a0\u00a0objectMapper.readerForUpdating(ownerDto) \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.readValue(patchNode); \u00a0\u00a0\u00a0ownerMapper.updateWithNull(ownerDto, owner);  \u00a0\u00a0\u00a0Owner resultOwner = ownerRepository.save(owner); \u00a0\u00a0\u00a0return ownerMapper.toOwnerDto(resultOwner); }<\/code><\/pre>\n<p>\u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0442\u0435\u0441\u0442\u0430 \u043d\u0430\u043c \u0442\u043e\u0436\u0435 \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u044f\u0442\u0441\u044f \u0432\u0445\u043e\u0434\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0432\u0441\u0442\u0430\u0432\u0438\u043c \u0441\u0442\u0440\u043e\u043a\u0438, \u043e\u0431\u0440\u0430\u0449\u0430\u044e\u0449\u0438\u0435\u0441\u044f \u043a \u0443\u0436\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u043d\u044b\u043c\u0438 \u0440\u0430\u043d\u0435\u0435 SQL-\u0444\u0430\u0439\u043b\u0430\u043c.<\/p>\n<pre><code class=\"java\">@Sql(scripts = \"classpath:insert-owners.sql\",       executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD ) @Sql(scripts = \"classpath:delete-owners.sql\",      executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD )<\/code><\/pre>\n<p>\u0421\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u043c \u0443\u0436\u0435 \u0437\u043d\u0430\u043a\u043e\u043c\u044b\u0439 \u043d\u0430\u043c JSON \u043f\u0440\u0438 \u043f\u043e\u043c\u043e\u0449\u0438 LLM. \u041f\u043e\u0442\u043e\u043c \u043f\u043e\u043c\u0435\u043d\u044f\u0435\u043c \u0438\u043c\u044f \u043d\u0430 <code>Johny<\/code>, \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043c \u0430\u0434\u0440\u0435\u0441 \u0432 <code>null<\/code> \u0438 \u043f\u0435\u0440\u0435\u0438\u043c\u0435\u043d\u0443\u0435\u043c \u0433\u043e\u0440\u043e\u0434 \u0432 <code>Spring Ville<\/code>. \u041e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043c\u044b \u043c\u0435\u043d\u044f\u0442\u044c \u043d\u0435 \u0445\u043e\u0442\u0438\u043c, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043f\u0440\u043e\u0441\u0442\u043e \u0443\u0434\u0430\u043b\u0438\u043c \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0435 \u0441\u0442\u0440\u043e\u043a\u0438 \u0438\u0437 \u043a\u043e\u0434\u0430 \u0442\u0435\u0441\u0442\u0430. \u0422\u0430\u043a\u0436\u0435 \u0443\u043a\u0430\u0436\u0435\u043c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043f\u043e\u043b\u044f <code>id<\/code>.<\/p>\n<p>\u041e\u0441\u0442\u0430\u043d\u0435\u0442\u0441\u044f \u043b\u0438\u0448\u044c \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c expect\u2019\u044b. \u0418\u0445 \u0442\u043e\u0436\u0435 \u043f\u043e\u0434\u0441\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 LLM, \u043d\u043e \u0437\u0430 \u043d\u0435\u0439 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0432\u043d\u0438\u043c\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u0441\u043b\u0435\u0434\u0438\u0442\u044c, \u0447\u0442\u043e\u0431\u044b \u0443\u0431\u0435\u0434\u0438\u0442\u044c\u0441\u044f, \u0447\u0442\u043e \u0432\u0441\u0435 \u0435\u0435 \u043f\u043e\u0434\u0441\u043a\u0430\u0437\u043a\u0438 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u0435. \u0422\u0430\u043a\u0436\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c, \u0447\u0442\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0432 \u0431\u0430\u0437\u0435 \u0442\u043e\u0436\u0435 \u043f\u043e\u043c\u0435\u043d\u044f\u043b\u0438\u0441\u044c. \u041e\u043a\u043e\u043d\u0447\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u0432\u0438\u0434 \u0442\u0435\u0441\u0442\u0430 \u0434\u043b\u044f \u043c\u0435\u0442\u043e\u0434\u0430 PATCH \u0431\u0443\u0434\u0435\u0442 \u0442\u0430\u043a\u0438\u043c:<\/p>\n<pre><code class=\"java\">@Test @Sql(scripts = \"classpath:insert-owners.sql\", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) @Sql(scripts = \"classpath:delete-owners.sql\", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) public void patch() throws Exception {  String patchNode = \"\"\" \u00a0\u00a0\u00a0\u00a0{ \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"id\": 1, \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"firstName\": \"Johny\", \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"address\": null, \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"city\": \"Spring Ville\" \u00a0\u00a0\u00a0\u00a0}\"\"\";  mockMvc.perform(MockMvcRequestBuilders.patch(\"\/rest\/owners\/{0}\", \"1\") \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.content(patchNode) \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.contentType(MediaType.APPLICATION_JSON)) \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.andExpect(status().isOk()) \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.andExpect(jsonPath(\"$.firstName\").value(\"Johny\")) \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.andExpect(jsonPath(\"$.address\").value(nullValue())) \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.andExpect(jsonPath(\"$.city\").value(\"Spring Ville\")) \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.andExpect(jsonPath(\"$.telephone\").value(\"5555555555\")) \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.andExpect(jsonPath(\"$.id\").value(1)) \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.andExpect(jsonPath(\"$.lastName\").value(\"Doe\"));  Owner owner = ownerRepository.findById(1) \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.orElseThrow();    assertThat(owner.getFirstName()).isEqualTo(\"Johny\"); assertThat(owner.getAddress()).isNull(); assertThat(owner.getCity()).isEqualTo(\"Spring Ville\"); assertThat(owner.getTelephone()).isEqualTo(\"5555555555\"); assertThat(owner.getLastName()).isEqualTo(\"Doe\"); }<\/code><\/pre>\n<h3>\u0422\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c DELETE-\u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442<\/h3>\n<pre><code class=\"java\">@DeleteMapping(\"\/{id}\") public OwnerDto delete(@PathVariable Integer id) { \u00a0\u00a0\u00a0Owner owner = ownerRepository.findById(id) \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.orElse(null);  \u00a0\u00a0\u00a0if (owner != null) { \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0ownerRepository.delete(owner); \u00a0\u00a0\u00a0}  \u00a0\u00a0\u00a0return ownerMapper.toOwnerDto(owner); }<\/code><\/pre>\n<p>\u0413\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u043c \u0442\u0435\u0441\u0442, \u0432\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u043c \u0443\u0436\u0435 \u0445\u043e\u0440\u043e\u0448\u043e \u0437\u043d\u0430\u043a\u043e\u043c\u044b\u0435 \u0434\u0432\u0435 \u0441\u0442\u0440\u043e\u043a\u0438 \u0434\u043b\u044f \u043e\u0431\u0440\u0430\u0449\u0435\u043d\u0438\u044f \u043a SQL-\u0444\u0430\u0439\u043b\u0430\u043c, \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c, \u0447\u0442\u043e \u0437\u0430\u043f\u0438\u0441\u044c, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u043c\u044b \u0443\u0434\u0430\u043b\u044f\u0435\u043c \u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u043d\u0430\u0439\u0434\u0435\u043d\u0430. \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u0442\u0441\u044f \u0432\u043e\u0442 \u0442\u0430\u043a\u043e\u0439 \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u043a\u043e\u0440\u043e\u0442\u043a\u0438\u0439 \u0442\u0435\u0441\u0442:<\/p>\n<pre><code class=\"java\">@Test @Sql(scripts = \"classpath:insert-owners.sql\", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) @Sql(scripts = \"classpath:delete-owners.sql\", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) public void delete() throws Exception { mockMvc.perform(MockMvcRequestBuilders.delete(\"\/rest\/owners\/{0}\", \"1\")) \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.andExpect(status().isOk()); assertThat(ownerRepository.findById(1)).isNotPresent(); }<\/code><\/pre>\n<p>\u0420\u0435\u0448\u0438\u0442\u044c \u043f\u043e\u0432\u0441\u0435\u0434\u043d\u0435\u0432\u043d\u0443\u044e \u0437\u0430\u0434\u0430\u0447\u0443 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u0442\u0435\u0441\u0442\u043e\u0432 \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0435\u0439 Amplcode \u0438 \u043f\u043e\u0434\u0441\u043a\u0430\u0437\u043e\u043a \u043e\u0442 \u0418\u0418 \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u043e\u0441\u044c \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0431\u044b\u0441\u0442\u0440\u043e. \u041e\u0441\u0442\u0430\u043b\u043e\u0441\u044c \u043b\u0438\u0448\u044c \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0442\u0435\u0441\u0442\u044b \u0438 \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u0438\u0442\u044c\u0441\u044f, \u0447\u0442\u043e \u0432\u0441\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442.\u00a0<\/p>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/139\/077\/7e8\/1390777e8ee343dc3151e89327608e7f.png\" width=\"1600\" height=\"639\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/139\/077\/7e8\/1390777e8ee343dc3151e89327608e7f.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/139\/077\/7e8\/1390777e8ee343dc3151e89327608e7f.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<h3>\u0417\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435<\/h3>\n<p>\u0421\u0435\u0433\u043e\u0434\u043d\u044f \u043c\u044b \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0435\u043b\u0438, \u043a\u0430\u043a \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u044b\u0435 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0435 \u0442\u0435\u0441\u0442\u044b \u0434\u043b\u044f REST API \u043d\u0430 Spring Boot: \u043e\u0442 \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 \u0442\u0435\u0441\u0442\u043e\u0432 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e Amplicode \u0434\u043e \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f \u0447\u0435\u0440\u0435\u0437 Docker Compose Starter, \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0432 \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044f\u043c\u0438 LLM. \u0412 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0445 \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u0430\u0445 \u043c\u044b \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u043c \u0442\u0435\u043c\u0443 \u0438 \u043f\u043e\u043a\u0430\u0436\u0435\u043c \u0430\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u043d\u044b\u0439 \u043f\u043e\u0434\u0445\u043e\u0434 \u043a \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0435 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f \u2014 \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c Testcontainers.<\/p>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/9c0\/89d\/66e\/9c089d66ef1a4d15982561de8b9d5efc.png\" alt=\"\u0420\u0438\u0441\u0443\u043d\u043e\u043a\" title=\"\u0420\u0438\u0441\u0443\u043d\u043e\u043a\" width=\"1560\" height=\"333\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/9c0\/89d\/66e\/9c089d66ef1a4d15982561de8b9d5efc.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/9c0\/89d\/66e\/9c089d66ef1a4d15982561de8b9d5efc.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/p>\n<div><figcaption>\u0420\u0438\u0441\u0443\u043d\u043e\u043a<\/figcaption><\/div>\n<\/figure>\n<p>\u041f\u043e\u0434\u043f\u0438\u0441\u044b\u0432\u0430\u0439\u0442\u0435\u0441\u044c \u043d\u0430 \u043d\u0430\u0448\u0438\u00a0<a href=\"https:\/\/t.me\/+8RZ1V1mxrHw0MmQy\">Telegram<\/a>\u00a0\u0438\u00a0<a href=\"https:\/\/youtube.com\/@amplicode\">YouTube<\/a>, \u0447\u0442\u043e\u0431\u044b \u043d\u0435 \u043f\u0440\u043e\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u043d\u043e\u0432\u044b\u0435 \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u044b \u043f\u0440\u043e\u00a0<a href=\"https:\/\/amplicode.ru\/?utm_source=habr&amp;utm_medium=guide&amp;utm_campaign=liquibase\">Amplicode<\/a>, Spring \u0438 \u0441\u0432\u044f\u0437\u0430\u043d\u043d\u044b\u0435 \u0441 \u043d\u0438\u043c \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0438!\u00a0\u00a0<\/p>\n<p>\u0410 \u0435\u0441\u043b\u0438 \u0432\u044b \u0445\u043e\u0442\u0438\u0442\u0435 \u043f\u043e\u043f\u0440\u043e\u0431\u043e\u0432\u0430\u0442\u044c Amplicode \u0432 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0438 \u2013 \u0442\u043e \u043c\u043e\u0436\u0435\u0442\u0435 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0435\u0433\u043e \u0430\u0431\u0441\u043e\u043b\u044e\u0442\u043d\u043e \u0431\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u043e \u0443\u0436\u0435 \u0441\u0435\u0439\u0447\u0430\u0441, \u043a\u0430\u043a \u0432\u00a0<a href=\"https:\/\/amplicode.ru\/documentation\/installation-guide-intellij\/\">IntelliJ IDEA\/GigaIDE<\/a>, \u0442\u0430\u043a \u0438 \u0432\u00a0<a href=\"https:\/\/amplicode.ru\/documentation\/installation-guide-vs-code\/\">VS Code<\/a>.\u00a0<\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/910692\/\"> https:\/\/habr.com\/ru\/articles\/910692\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u041a\u043e\u0433\u0434\u0430 \u043e\u0434\u043d\u0438\u0445 \u044e\u043d\u0438\u0442-\u0442\u0435\u0441\u0442\u043e\u0432 \u0443\u0436\u0435 \u043d\u0435\u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e, \u043d\u0430 \u0441\u0446\u0435\u043d\u0443 \u0432\u044b\u0445\u043e\u0434\u044f\u0442 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0435. \u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u043e\u0442 \u043a\u043e\u043c\u0430\u043d\u0434\u044b <a href=\"https:\/\/amplicode.ru\/?utm_source=habr&amp;utm_medium=article&amp;utm_campaign=integration-testing-part1\">Amplicode<\/a> \u043c\u044b \u043f\u043e\u043a\u0430\u0436\u0435\u043c, \u043a\u0430\u043a \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c REST API \u0432 Spring Boot \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0433\u043e \u0441\u0442\u0435\u043a\u0430: \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f \u0442\u0435\u0441\u0442\u043e\u0432 \u0447\u0435\u0440\u0435\u0437 Amplicode, \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0437\u0430\u043f\u0443\u0441\u043a \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e Docker Compose Starter \u0438 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0438 \u0441\u043e \u0441\u0442\u043e\u0440\u043e\u043d\u044b LLM-\u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u043e\u0442 \u042f\u043d\u0434\u0435\u043a\u0441\u0430.<\/p>\n<hr\/>\n<p>\u0421\u0442\u0430\u0442\u044c\u044f \u0442\u0430\u043a\u0436\u0435 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u0430 \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435 \u0432\u0438\u0434\u0435\u043e \u043d\u0430\u00a0<a href=\"https:\/\/youtu.be\/suqpDTeHqSY\">YouTube<\/a>,\u00a0<a href=\"https:\/\/vk.com\/video-222549074_456239152\">VK \u0412\u0438\u0434\u0435\u043e<\/a>\u00a0\u0438\u00a0<a href=\"https:\/\/rutube.ru\/video\/130fb4f1b441e8416237d2315e798a6d\/\">RUTUBE<\/a>, \u0442\u0430\u043a \u0447\u0442\u043e \u043c\u043e\u0436\u043d\u043e \u0438 \u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c, \u0438 \u0447\u0438\u0442\u0430\u0442\u044c \u2014 \u043a\u0430\u043a \u0432\u0430\u043c \u0443\u0434\u043e\u0431\u043d\u0435\u0435!\u00a0<\/p>\n<div class=\"tm-iframe_temp\" data-src=\"https:\/\/embedd.srv.habr.com\/iframe\/682b1143488f69a80822b07e\" data-style=\"\" id=\"682b1143488f69a80822b07e\" width=\"\"><\/div>\n<hr\/>\n<p>\u0422\u0435\u043c\u0430 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043e\u0434\u043d\u0430 \u0438\u0437 \u0432\u0430\u0436\u043d\u0435\u0439\u0448\u0438\u0445 \u0434\u043b\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0438\u043c\u0435\u043d\u043d\u043e \u0431\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u044f \u0442\u0435\u0441\u0442\u0430\u043c \u043c\u044b \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u043f\u0440\u0435\u0438\u043c\u0443\u0449\u0435\u0441\u0442\u0432\u0430:<\/p>\n<ol>\n<li>\n<p>\u0423\u0432\u0435\u0440\u0435\u043d\u043d\u043e\u0441\u0442\u044c \u0432 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u0440\u0430\u0431\u043e\u0442\u044b \u043a\u043e\u0434\u0430<\/p>\n<\/li>\n<li>\n<p>\u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0433\u043e \u0440\u0435\u0444\u0430\u043a\u0442\u043e\u0440\u0438\u043d\u0433\u0430<\/p>\n<\/li>\n<li>\n<p>\u0421\u043e\u043a\u0440\u0430\u0449\u0435\u043d\u0438\u0435 \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u043d\u0430 \u043f\u043e\u0438\u0441\u043a \u043e\u0448\u0438\u0431\u043e\u043a<\/p>\n<\/li>\n<li>\n<p>\u0423\u043c\u0435\u043d\u044c\u0448\u0435\u043d\u0438\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0430 \u0434\u0435\u0444\u0435\u043a\u0442\u043e\u0432 \u0432 \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u0435\u043d\u0435<\/p>\n<\/li>\n<li>\n<p>\u0414\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f \u043e\u0436\u0438\u0434\u0430\u0435\u043c\u043e\u0433\u043e \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u044b<\/p>\n<\/li>\n<li>\n<p>\u0423\u043f\u0440\u043e\u0449\u0435\u043d\u0438\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0438 \u0438 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/p>\n<\/li>\n<li>\n<p>\u0411\u044b\u0441\u0442\u0440\u0430\u044f \u043e\u0431\u0440\u0430\u0442\u043d\u0430\u044f \u0441\u0432\u044f\u0437\u044c \u043e \u0440\u0430\u0431\u043e\u0442\u043e\u0441\u043f\u043e\u0441\u043e\u0431\u043d\u043e\u0441\u0442\u0438 \u0441\u0438\u0441\u0442\u0435\u043c\u044b<\/p>\n<\/li>\n<li>\n<p>\u041f\u043e\u0432\u044b\u0448\u0435\u043d\u0438\u0435 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0430 \u0440\u0430\u0437\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u043c\u043e\u0433\u043e \u043f\u0440\u043e\u0434\u0443\u043a\u0442\u0430<\/p>\n<\/li>\n<li>\n<p>\u041f\u043e\u0432\u044b\u0448\u0435\u043d\u0438\u0435 \u043d\u0430\u0434\u0435\u0436\u043d\u043e\u0441\u0442\u0438 \u0438 \u0441\u0442\u0430\u0431\u0438\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u0441\u0438\u0441\u0442\u0435\u043c\u044b<\/p>\n<\/li>\n<li>\n<p>\u041c\u043e\u0436\u0435\u0442\u0435 \u0441\u0430\u043c\u0438 \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u0442\u044c \u0432 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u044f\u0445, \u0435\u0441\u043b\u0438 \u044f \u0447\u0442\u043e-\u0442\u043e \u0437\u0430\u0431\u044b\u043b \ud83d\ude42<\/p>\n<\/li>\n<\/ol>\n<p>\u0410 \u0437\u0430 \u0441\u0447\u0451\u0442 \u0442\u043e\u0433\u043e, \u0447\u0442\u043e Spring \u043f\u0440\u0438\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f <a href=\"https:\/\/docs.spring.io\/spring-integration\/docs\/6.1.4\/reference\/html\/index-single.html#overview-components\">\u0441\u043b\u043e\u0435\u043d\u043e\u0439 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b<\/a>, \u0443 \u043d\u0430\u0441 \u043f\u043e\u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0432\u0435\u043b\u0438\u043a\u043e\u043b\u0435\u043f\u043d\u0430\u044f \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043a\u0430\u0436\u0434\u044b\u0439 \u0441\u043b\u043e\u0439 \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438. \u0421\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u0438 \u0438 \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0438 \u043c\u043e\u0433\u0443\u0442 \u043f\u043e\u043c\u043e\u0447\u044c \u0432 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u0438 \u044e\u043d\u0438\u0442 \u0442\u0435\u0441\u0442\u043e\u0432 \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0438\u0437 \u0443\u0440\u043e\u0432\u043d\u0435\u0439:<\/p>\n<figure class=\"full-width\"><\/figure>\n<p>\u041d\u043e \u0434\u0430\u0436\u0435 \u0435\u0441\u043b\u0438 \u043c\u044b \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c \u043a\u0430\u0436\u0434\u044b\u0439 \u0443\u0440\u043e\u0432\u0435\u043d\u044c \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438, \u043c\u044b \u043d\u0435 \u043c\u043e\u0436\u0435\u043c \u0431\u044b\u0442\u044c \u0443\u0432\u0435\u0440\u0435\u043d\u043d\u044b\u043c\u0438 \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e \u0432\u043c\u0435\u0441\u0442\u0435 \u044d\u0442\u0438 \u0443\u0440\u043e\u0432\u043d\u0438 \u0442\u043e\u0436\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0442 \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e.<\/p>\n<p>\u0420\u0435\u0448\u0438\u0442\u044c \u044d\u0442\u0443 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443 \u043f\u043e\u043c\u043e\u0433\u0430\u044e\u0442 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0435 \u0442\u0435\u0441\u0442\u044b, \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u044e\u0449\u0438\u0435 \u0440\u0430\u0431\u043e\u0442\u0443 \u0432\u0441\u0435\u0445 \u0443\u0440\u043e\u0432\u043d\u0435\u0439 \u0432\u043c\u0435\u0441\u0442\u0435, \u0430 \u0442\u0430\u043a\u0436\u0435 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u0441 \u0432\u043d\u0435\u0448\u043d\u0438\u043c\u0438 \u0441\u0435\u0440\u0432\u0438\u0441\u0430\u043c\u0438, \u0441\u0438\u0441\u0442\u0435\u043c\u0430\u043c\u0438 \u0438 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0430\u043c\u0438.<\/p>\n<h3>\u041e\u0431\u0437\u043e\u0440 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0438 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0430 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439<\/h3>\n<p>\u0421\u0435\u0433\u043e\u0434\u043d\u044f \u043c\u044b \u0432\u043e\u0437\u044c\u043c\u0451\u043c \u043d\u0435\u0431\u0435\u0437\u044b\u0437\u0432\u0435\u0441\u0442\u043d\u044b\u0439 \u043f\u0440\u043e\u0435\u043a\u0442 <a href=\"https:\/\/github.com\/Amplicode\/amplicode-tutorials\/tree\/main\/integration-testing-docker-compose\">spring petclinic<\/a> \u0438 \u043d\u0430\u043f\u0438\u0448\u0435\u043c \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0435 \u0442\u0435\u0441\u0442\u044b \u0434\u043b\u044f CRUD REST \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u0430.<\/p>\n<p>\u041a\u0430\u043a \u0432\u0441\u0435\u0433\u0434\u0430, \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u0441\u043d\u0430\u0447\u0430\u043b\u0430 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c, \u0441 \u043a\u0430\u043a\u0438\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435\u043c \u043d\u0430\u043c \u043f\u0440\u0438\u0434\u0435\u0442\u0441\u044f \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c.<\/p>\n<p>\u0418\u0437 \u0441\u0442\u0430\u0440\u0442\u0435\u0440\u043e\u0432 \u0443 \u043d\u0430\u0441 \u043f\u0440\u0438\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u044e\u0442 Spring Web, Spring Data JPA, \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u0432\u0435\u0440\u0441\u0438\u043e\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445 \u0432\u044b\u0431\u0440\u0430\u043d Flyway, \u0442\u0430\u043a\u0436\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u044b \u0430\u043a\u0442\u0443\u0430\u0442\u043e\u0440\u044b \u0438 Kafka, \u0430 \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f PostgreSQL.<\/p>\n<figure class=\"full-width\"><\/figure>\n<p>\u0422\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043c\u044b \u0441\u0435\u0433\u043e\u0434\u043d\u044f \u0431\u0443\u0434\u0435\u043c <code>OwnerRestController<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435 \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u0447\u0443\u0442\u044c \u043f\u043e\u0437\u0436\u0435.<\/p>\n<p>\u041e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u0445\u043e\u0442\u0435\u043b \u0431\u044b \u0443\u0434\u0435\u043b\u0438\u0442\u044c \u0442\u043e\u043c\u0443, \u043a\u0430\u043a \u0443\u0441\u0442\u0440\u043e\u0435\u043d\u0430 \u0440\u0430\u0431\u043e\u0442\u0430 \u0441 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u0435\u043c \u043d\u0430 \u044d\u0442\u043e\u043c \u043f\u0440\u043e\u0435\u043a\u0442\u0435.<\/p>\n<figure class=\"full-width\"><\/figure>\n<p>\u0415\u0441\u0442\u044c \u0444\u0430\u0439\u043b services.yaml, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u043e\u0431\u044a\u044f\u0432\u043b\u0435\u043d\u044b \u0441\u0435\u0440\u0432\u0438\u0441\u044b Kafka \u0438 PostgreSQL, \u0430 \u0442\u0430\u043a\u0436\u0435 \u0435\u0441\u0442\u044c docker compose \u0444\u0430\u0439\u043b\u044b \u0434\u043b\u044f \u0434\u0435\u0432\u0435\u043b\u043e\u043f\u043c\u0435\u043d\u0442\u0430 \u0438 \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u0435\u043d\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0440\u0430\u0441\u0448\u0438\u0440\u044f\u044e\u0442 \u0441\u0435\u0440\u0432\u0438\u0441\u044b \u0438\u0437 \u0444\u0430\u0439\u043b\u0430 services.yaml. \u041f\u0440\u043e \u0442\u0430\u043a\u043e\u0439 \u0441\u043f\u043e\u0441\u043e\u0431 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f \u044f \u0443\u0436\u0435 \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u044b\u0432\u0430\u043b \u0432 \u0441\u0442\u0430\u0442\u044c\u0435 &#171;<a href=\"https:\/\/habr.com\/ru\/companies\/haulmont\/articles\/848696\/\">\u041b\u0443\u0447\u0448\u0438\u0439 \u0441\u043f\u043e\u0441\u043e\u0431 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u0439 \u0434\u043b\u044f Spring Boot \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e Docker Compose<\/a>&#171;.<\/p>\n<p>\u041f\u0435\u0440\u0435\u0439\u0434\u0435\u043c \u043a \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u0443.<\/p>\n<details class=\"spoiler\">\n<summary>OwnerRestController<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"java\">@RestController @RequestMapping(\"\/rest\/owners\") public class OwnerRestController {  \u00a0\u00a0\u00a0private final OwnerRepository ownerRepository; \u00a0\u00a0\u00a0private final OwnerMapper ownerMapper; \u00a0\u00a0\u00a0private final ObjectMapper objectMapper;  \u00a0\u00a0\u00a0public OwnerRestController(OwnerRepository ownerRepository, \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0OwnerMapper ownerMapper, \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0ObjectMapper objectMapper) { \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0this.ownerRepository = ownerRepository; \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0this.ownerMapper = ownerMapper; \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0this.objectMapper = objectMapper; \u00a0\u00a0\u00a0}  \u00a0\u00a0\u00a0@PostMapping \u00a0\u00a0\u00a0public OwnerDto create(@RequestBody @Valid OwnerDto ownerDto) { \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0if (ownerDto.getId() != null) { \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0throw new ResponseStatusException(HttpStatus.UNPROCESSABLE_ENTITY, \"Id must be null\"); \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0} \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Owner owner = ownerMapper.toEntity(ownerDto); \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Owner saved = ownerRepository.save(owner); \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0return ownerMapper.toOwnerDto(saved); \u00a0\u00a0\u00a0}  \u00a0\u00a0\u00a0@GetMapping(\"\/{id}\") \u00a0\u00a0\u00a0public OwnerMinimalDto getOne(@PathVariable Integer id) { \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Optional&lt;Owner&gt; ownerOptional = ownerRepository.findById(id); \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0return ownerMapper.toOwnerMinimalDto(ownerOptional.orElseThrow(() -&gt; \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0new ResponseStatusException(HttpStatus.NOT_FOUND, \"Entity with id %s not found\".formatted(id)))); \u00a0\u00a0\u00a0}  \u00a0\u00a0\u00a0@GetMapping \u00a0\u00a0\u00a0public PagedModel&lt;OwnerMinimalDto&gt; getAll(@ModelAttribute OwnerFilter ownerFilter, \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Pageable pageable) { \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Page&lt;Owner&gt; owners = ownerRepository.findAll(ownerFilter.toSpecification(), pageable); \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0var ownerDtoPage = owners.map(ownerMapper::toOwnerMinimalDto); \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0return new PagedModel&lt;&gt;(ownerDtoPage); \u00a0\u00a0\u00a0}   \u00a0\u00a0\u00a0@PutMapping(\"\/{id}\") \u00a0\u00a0\u00a0public OwnerDto update(@PathVariable Integer id, \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0@RequestBody @Valid OwnerDto dto) { \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0if (!dto.getId().equals(id)) { \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0throw new ResponseStatusException(HttpStatus.BAD_REQUEST, \"Id in request body and path variable must be equal\"); \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}  \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Owner owner = ownerRepository.findById(id) \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.orElseThrow(() -&gt; \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0new ResponseStatusException(HttpStatus.NOT_FOUND, \"Entity with id %s not found\".formatted(id)) \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0); \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0ownerMapper.updateWithNull(dto, owner); \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Owner resultOwner = ownerRepository.save(owner); \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0return ownerMapper.toOwnerDto(resultOwner); \u00a0\u00a0\u00a0}  \u00a0\u00a0\u00a0@PatchMapping(\"\/{id}\") \u00a0\u00a0\u00a0public OwnerDto patch(@PathVariable Integer id, \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0@RequestBody JsonNode patchNode) throws IOException { \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0if (patchNode.get(\"id\") == null || patchNode.get(\"id\").asInt() != id) { \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0throw new ResponseStatusException(HttpStatus.BAD_REQUEST, \"Id in request body and path variable must be equal\"); \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}  \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Owner owner = ownerRepository.findById(id) \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.orElseThrow(() -&gt; \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0new ResponseStatusException(HttpStatus.NOT_FOUND, \"Entity with id %s not found\".formatted(id)) \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0); \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0OwnerDto ownerDto = ownerMapper.toOwnerDto(owner);  \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0objectMapper.readerForUpdating(ownerDto) \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.readValue(patchNode); \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0ownerMapper.updateWithNull(ownerDto, owner); \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Owner resultOwner = ownerRepository.save(owner); \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0return ownerMapper.toOwnerDto(resultOwner); \u00a0\u00a0\u00a0}  \u00a0\u00a0\u00a0@DeleteMapping(\"\/{id}\") \u00a0\u00a0\u00a0public OwnerDto delete(@PathVariable Integer id) { \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Owner owner = ownerRepository.findById(id) \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.orElse(null); \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0if (owner != null) { \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0ownerRepository.delete(owner); \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0} \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0return ownerMapper.toOwnerDto(owner); \u00a0\u00a0\u00a0}  }<\/code><\/pre>\n<\/div>\n<\/details>\n<p>\u041d\u0430 \u0441\u0430\u043c\u043e\u043c \u0434\u0435\u043b\u0435 \u044d\u0442\u043e\u0442 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440 \u0431\u044b\u043b \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u043d \u0440\u0430\u043d\u0435\u0435 \u0434\u043b\u044f <a href=\"https:\/\/habr.com\/ru\/companies\/haulmont\/articles\/866060\/\">\u0441\u0442\u0430\u0442\u044c\u0438 &#171;\u0421\u043e\u0437\u0434\u0430\u0451\u043c CRUD REST API \u0432 Spring Boot \u0431\u044b\u0441\u0442\u0440\u043e \u0438 \u043f\u0440\u043e\u0441\u0442\u043e \u0432\u043c\u0435\u0441\u0442\u0435 \u0441 Amplicode&#187;<\/a>, \u0438 \u0435\u0433\u043e \u0440\u0430\u0431\u043e\u0442\u043e\u0441\u043f\u043e\u0441\u043e\u0431\u043d\u043e\u0441\u0442\u044c \u0431\u044b\u043b\u0430 \u043f\u0440\u043e\u0432\u0435\u0440\u0435\u043d\u0430 \u0443\u0436\u0435 \u0442\u043e\u0433\u0434\u0430 \u043f\u0440\u0438 \u043f\u043e\u043c\u043e\u0449\u0438 <a href=\"https:\/\/habr.com\/ru\/companies\/haulmont\/articles\/868106\/\">ConneKt (HTTP \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u043e\u0442 Amplicode)<\/a>. \u0422\u043e\u0433\u0434\u0430 \u0436\u0435 \u0431\u044b\u043b\u043e \u043f\u043e\u043a\u0430\u0437\u0430\u043d\u043e, \u043a\u0430\u043a \u0444\u043e\u0440\u043c\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043a \u044d\u0442\u043e\u043c\u0443 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u0443 \u0438 \u043f\u0438\u0441\u0430\u0442\u044c \u043a\u043e\u043c\u0430\u043d\u0434\u044b assert \u0434\u043b\u044f \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u043e\u0432.\u00a0<\/p>\n<figure class=\"full-width\"><\/figure>\n<p>\u041d\u043e \u044d\u0442\u043e \u0432\u0441\u0435 \u0436\u0435 \u0431\u044b\u043b\u043e \u0441\u043a\u043e\u0440\u0435\u0435 \u0440\u0443\u0447\u043d\u043e\u0435, \u0438\u043b\u0438, \u0442\u043e\u0447\u043d\u0435\u0435, \u043f\u043e\u043b\u0443\u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0435 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435. \u0421\u0430\u043c\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0438 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u043d\u0435\u0433\u043e \u043f\u043e\u0434\u043d\u0438\u043c\u0430\u043b\u0438\u0441\u044c \u0432\u0440\u0443\u0447\u043d\u0443\u044e. \u041e\u0434\u043d\u0430\u043a\u043e, \u043b\u044e\u0431\u043e\u043c\u0443 production-ready \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044e \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0435 \u0442\u0435\u0441\u0442\u044b. \u041f\u0440\u0438\u0441\u0442\u0443\u043f\u0438\u043c \u043a\u00a0 \u0438\u0445 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044e.\u00a0<\/p>\n<h4>\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u043e\u0432<\/h4>\n<p>\u041e\u0447\u0435\u0432\u0438\u0434\u043d\u043e, \u0447\u0442\u043e \u043c\u044b \u043d\u0435 \u0445\u043e\u0442\u0438\u043c \u0432\u0440\u0443\u0447\u043d\u0443\u044e \u043f\u043e\u0434\u043d\u0438\u043c\u0430\u0442\u044c \u0431\u0430\u0437\u0443 \u043a\u0430\u0436\u0434\u044b\u0439 \u0440\u0430\u0437, \u043a\u043e\u0433\u0434\u0430 \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0442\u0435\u0441\u0442\u044b. \u0425\u043e\u0442\u0435\u043b\u043e\u0441\u044c \u0431\u044b, \u0447\u0442\u043e\u0431\u044b \u0432\u0441\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u0441\u0435\u0440\u0432\u0438\u0441\u044b \u0441\u0442\u0430\u0440\u0442\u043e\u0432\u0430\u043b\u0438 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043a\u0430\u0436\u0434\u044b\u0439 \u0440\u0430\u0437, \u043a\u043e\u0433\u0434\u0430 \u044f \u0431\u0443\u0434\u0443 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c \u0442\u0435\u0441\u0442\u044b. \u0417\u0434\u0435\u0441\u044c \u0442\u043e\u0436\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u0434\u0445\u043e\u0434\u043e\u0432, \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <a href=\"https:\/\/testcontainers.com\/\">Testcontainers<\/a>, \u043d\u043e \u0441\u0435\u0433\u043e\u0434\u043d\u044f \u043c\u044b \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0435\u043c \u043f\u0440\u043e <a href=\"https:\/\/spring.io\/blog\/2023\/06\/21\/docker-compose-support-in-spring-boot-3-1\">Docker Compose Starter<\/a>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u044f\u0432\u0438\u043b\u0441\u044f \u043d\u0435 \u0442\u0430\u043a \u0434\u0430\u0432\u043d\u043e, \u0432 Spring Boot 3.1.<\/p>\n<p>\u041c\u044b \u043c\u043e\u0436\u0435\u043c \u0434\u043b\u044f \u043d\u0430\u0448\u0438\u0445 \u0442\u0435\u0441\u0442\u043e\u0432 \u0443\u043a\u0430\u0437\u0430\u0442\u044c docker compose \u0444\u0430\u0439\u043b, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0431\u0443\u0434\u0443\u0442 \u043e\u0431\u044a\u044f\u0432\u043b\u0435\u043d\u044b \u0432\u0441\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u043d\u0430\u043c \u0441\u0435\u0440\u0432\u0438\u0441\u044b, \u0438 \u0442\u043e\u0433\u0434\u0430 \u0432 \u043c\u043e\u043c\u0435\u043d\u0442 \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0432\u0441\u0435 \u044d\u0442\u0438 \u0441\u0435\u0440\u0432\u0438\u0441\u044b \u0431\u0443\u0434\u0443\u0442 \u043f\u043e\u0434\u043d\u0438\u043c\u0430\u0442\u044c\u0441\u044f \u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f.<\/p>\n<p>\u0412 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438 \u0443\u0436\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442 \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0441\u0435\u0440\u044c\u0435\u0437\u043d\u0430\u044f \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f docker compose \u0444\u0430\u0439\u043b\u043e\u0432, \u0438 \u044d\u0442\u0438\u043c \u0444\u0430\u043a\u0442\u043e\u043c \u0433\u0440\u0435\u0445 \u043d\u0435 \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f.<\/p>\n<p>\u041f\u0435\u0440\u0432\u044b\u043c \u0434\u0435\u043b\u043e\u043c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u043c \u0441\u0442\u0430\u0440\u0442\u0435\u0440. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043f\u0435\u0440\u0435\u0439\u0434\u0435\u043c \u0432 build.gradle \u0438 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u0443\u044e \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044c.<\/p>\n<pre><code class=\"kotlin\">dependencies { implementation ( \u00a0\u00a0\u00a0\u00a0'org.springframework.boot:spring-boot-docker-compose' ) }<\/code><\/pre>\n<p>\u0418 \u0442\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043e\u0442\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044e \u0441 docker compose, \u043a\u0430\u043a \u0431\u044b \u0441\u0442\u0440\u0430\u043d\u043d\u043e \u044d\u0442\u043e \u043d\u0438 \u0437\u0432\u0443\u0447\u0430\u043b\u043e. \u0412 \u043f\u0440\u043e\u0442\u0438\u0432\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0431\u044b\u043b\u043e \u0431\u044b \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0442\u044c \u0435\u0435 \u0434\u043b\u044f \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0445 \u043f\u0440\u043e\u0444\u0438\u043b\u0435\u0439 \u2014 \u043f\u0440\u043e\u0434, \u0434\u0435\u0432, \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c, \u043a\u0430\u043a\u043e\u0439 \u0444\u0430\u0439\u043b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0434\u043b\u044f \u043a\u0430\u043a\u043e\u0433\u043e \u043f\u0440\u043e\u0444\u0438\u043b\u044f, \u0447\u0442\u043e \u0432\u044b\u0445\u043e\u0434\u0438\u0442 \u0437\u0430 \u0440\u0430\u043c\u043a\u0438 \u0434\u0430\u043d\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0438.<\/p>\n<p>\u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u043f\u0435\u0440\u0435\u0439\u0434\u0435\u043c \u0432 <a href=\"http:\/\/application.properties\">application.properties<\/a> \u0438 \u043d\u0430\u043f\u0438\u0448\u0435\u043c \u0442\u0430\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0435:<\/p>\n<pre><code class=\"powershell\">spring.docker.compose.enabled=false<\/code><\/pre>\n<p>\u0414\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043d\u0430\u0448\u0435\u0433\u043e \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u0430 \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043a\u043b\u0430\u0441\u0441 <code>OwnerRestControllerTest<\/code> \u0438 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u0438:<\/p>\n<pre><code class=\"java\">@SpringBootTest @TestPropertySource(properties = { \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"spring.docker.compose.enabled=true\", \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"spring.docker.compose.skip.in-tests=false\", \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"spring.docker.compose.stop.command=down\", \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"spring.docker.compose.file=docker-compose-tests.yaml\" }) @AutoConfigureMockMvc public class OwnerRestControllerTest {     ... } <\/code><\/pre>\n<p>\u0417\u0430\u043e\u0441\u0442\u0440\u044f\u0442\u044c \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043d\u0430 \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0442\u0440\u0438\u0432\u0438\u0430\u043b\u044c\u043d\u044b\u0445 \u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u044f\u0445 <code>@SpringBootTest<\/code> \u0438 <code>@AutoConfigureMockMvc<\/code> \u044f \u043d\u0435 \u0431\u0443\u0434\u0443. \u0420\u0430\u0441\u0441\u043a\u0430\u0436\u0443 \u043f\u043e\u043f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u0440\u043e <code>@TestPropertySource<\/code>.<\/p>\n<p>\u0417\u0434\u0435\u0441\u044c \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u043e <code>spring.docker.compose.enabled<\/code> \u0440\u0430\u0437\u0440\u0435\u0448\u0430\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 docker compose \u0444\u0430\u0439\u043b\u043e\u0432 \u0432 \u043c\u043e\u043c\u0435\u043d\u0442 \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0442\u0435\u0441\u0442\u043e\u0432. \u0412\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u044d\u0442\u0443 \u0444\u0438\u0447\u0443 \u044f\u0432\u043d\u043e \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043f\u043e\u0442\u043e\u043c\u0443, \u0447\u0442\u043e \u043c\u044b \u0435\u0451 \u0440\u0430\u043d\u0435\u0435 \u0432\u044b\u043a\u043b\u044e\u0447\u0438\u043b\u0438 \u0432 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u043c \u0444\u0430\u0439\u043b\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f <code>application.properties<\/code>.<\/p>\n<p>\u0421\u0432\u043e\u0439\u0441\u0442\u0432\u043e <code>stop.command<\/code> \u0432\u044b\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0432 <code>down<\/code> \u0432\u043c\u0435\u0441\u0442\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e <code>scope<\/code> \u0432\u043e \u0438\u0437\u0431\u0435\u0436\u0430\u043d\u0438\u0435 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u043c\u0435\u0436\u0434\u0443 \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u043c\u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u043c\u0438 \u0442\u0435\u0441\u0442\u043e\u0432, \u0431\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u044f \u044d\u0442\u043e\u043c\u0443 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0443 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440 \u043f\u043e\u0441\u043b\u0435 \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0432\u0441\u0435\u0445 \u0442\u0435\u0441\u0442\u043e\u0432 \u0431\u0443\u0434\u0435\u0442 \u0443\u043d\u0438\u0447\u0442\u043e\u0436\u0430\u0442\u044c\u0441\u044f, \u0438 \u0431\u0443\u0434\u0435\u0442 \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c\u0441\u044f \u043d\u043e\u0432\u044b\u0439.\u00a0<\/p>\n<p>\u041f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u043c \u0448\u0430\u0433\u043e\u043c \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0444\u0430\u0439\u043b\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0434\u043b\u044f \u043d\u0430\u0448\u0438\u0445 \u0442\u0435\u0441\u0442\u043e\u0432: <code>docker-compose-tests.yaml<\/code>.\u00a0<\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u043c \u043e\u0441\u0442\u0430\u0435\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u044d\u0442\u043e\u0442 docker compose \u0444\u0430\u0439\u043b.<\/p>\n<details class=\"spoiler\">\n<summary>docker-compose-tests.yaml \u0444\u0430\u0439\u043b<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"yaml\">services: \u00a0\u00a0postgres-test: \u00a0\u00a0\u00a0\u00a0extends: \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0service: postgres \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0file: services.yaml \u00a0\u00a0\u00a0\u00a0ports: \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0- \"5432:5432\" \u00a0\u00a0\u00a0\u00a0environment: \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0POSTGRES_PASSWORD: postgres \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0POSTGRES_DB: postgres \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0POSTGRES_USER: postgres  \u00a0\u00a0kafka-test: \u00a0\u00a0\u00a0\u00a0extends: \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0service: kafka \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0file: services.yaml \u00a0\u00a0\u00a0\u00a0ports: \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0- \"9094:9094\" \u00a0\u00a0\u00a0\u00a0environment: \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0KAFKA_ADVERTISED_LISTENERS: PLAINTEXT:\/\/kafka-test:29094,PLAINTEXT_HOST:\/\/localhost:9094 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0KAFKA_LISTENERS:<\/code><\/pre>\n<\/div>\n<\/details>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-460683","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/460683","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=460683"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/460683\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=460683"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=460683"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=460683"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}