{"id":88809,"date":"2025-07-06T21:49:30","date_gmt":"2025-07-06T14:49:30","guid":{"rendered":"https:\/\/itviec.com\/blog\/?p=88809"},"modified":"2025-07-06T21:49:34","modified_gmt":"2025-07-06T14:49:34","slug":"huong-dan-spring-boot-tutorial","status":"publish","type":"post","link":"https:\/\/itviec.com\/blog\/huong-dan-spring-boot-tutorial\/","title":{"rendered":"Spring Boot tutorial chi ti\u1ebft t\u1eeb A-Z cho l\u1eadp tr\u00ecnh vi\u00ean Java"},"content":{"rendered":"<div id=\"ez-toc-container\" class=\"ez-toc-v2_0_85 counter-hierarchy ez-toc-counter ez-toc-grey ez-toc-container-direction\">\n<div class=\"ez-toc-title-container\">\n<p class=\"ez-toc-title\" style=\"cursor:inherit\">N\u1ed9i dung b\u00e0i vi\u1ebft<\/p>\n<span class=\"ez-toc-title-toggle\"><a href=\"#\" class=\"ez-toc-pull-right ez-toc-btn ez-toc-btn-xs ez-toc-btn-default ez-toc-toggle\" aria-label=\"Toggle Table of Content\"><span class=\"ez-toc-js-icon-con\"><span class=\"\"><span class=\"eztoc-hide\" style=\"display:none;\">Toggle<\/span><span class=\"ez-toc-icon-toggle-span\"><svg style=\"fill: #999;color:#999\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" class=\"list-377408\" width=\"20px\" height=\"20px\" viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M6 6H4v2h2V6zm14 0H8v2h12V6zM4 11h2v2H4v-2zm16 0H8v2h12v-2zM4 16h2v2H4v-2zm16 0H8v2h12v-2z\" fill=\"currentColor\"><\/path><\/svg><svg style=\"fill: #999;color:#999\" class=\"arrow-unsorted-368013\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"10px\" height=\"10px\" viewBox=\"0 0 24 24\" version=\"1.2\" baseProfile=\"tiny\"><path d=\"M18.2 9.3l-6.2-6.3-6.2 6.3c-.2.2-.3.4-.3.7s.1.5.3.7c.2.2.4.3.7.3h11c.3 0 .5-.1.7-.3.2-.2.3-.5.3-.7s-.1-.5-.3-.7zM5.8 14.7l6.2 6.3 6.2-6.3c.2-.2.3-.5.3-.7s-.1-.5-.3-.7c-.2-.2-.4-.3-.7-.3h-11c-.3 0-.5.1-.7.3-.2.2-.3.5-.3.7s.1.5.3.7z\"\/><\/svg><\/span><\/span><\/span><\/a><\/span><\/div>\n<nav><ul class='ez-toc-list ez-toc-list-level-1 ' ><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-1\" href=\"https:\/\/itviec.com\/blog\/huong-dan-spring-boot-tutorial\/#Huong_dan_cai_dat_moi_truong_phat_trien\" >H\u01b0\u1edbng d\u1eabn c\u00e0i \u0111\u1eb7t m\u00f4i tr\u01b0\u1eddng ph\u00e1t tri\u1ec3n<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-2\" href=\"https:\/\/itviec.com\/blog\/huong-dan-spring-boot-tutorial\/#Huong_dan_tao_du_an_Spring_Boot_dau_tien\" >H\u01b0\u1edbng d\u1eabn t\u1ea1o d\u1ef1 \u00e1n Spring Boot \u0111\u1ea7u ti\u00ean<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-3\" href=\"https:\/\/itviec.com\/blog\/huong-dan-spring-boot-tutorial\/#Huong_dan_xay_dung_RESTful_API_voi_Spring_Boot\" >H\u01b0\u1edbng d\u1eabn x\u00e2y d\u1ef1ng RESTful API v\u1edbi Spring Boot<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-4\" href=\"https:\/\/itviec.com\/blog\/huong-dan-spring-boot-tutorial\/#Huong_dan_lam_viec_voi_co_so_du_lieu\" >H\u01b0\u1edbng d\u1eabn l\u00e0m vi\u1ec7c v\u1edbi c\u01a1 s\u1edf d\u1eef li\u1ec7u<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-5\" href=\"https:\/\/itviec.com\/blog\/huong-dan-spring-boot-tutorial\/#Huong_dan_bao_mat_ung_dung_voi_Spring_Security\" >H\u01b0\u1edbng d\u1eabn b\u1ea3o m\u1eadt \u1ee9ng d\u1ee5ng v\u1edbi Spring Security&nbsp;<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-6\" href=\"https:\/\/itviec.com\/blog\/huong-dan-spring-boot-tutorial\/#Huong_dan_testing_trong_Spring_Boot\" >H\u01b0\u1edbng d\u1eabn testing trong Spring Boot<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-7\" href=\"https:\/\/itviec.com\/blog\/huong-dan-spring-boot-tutorial\/#Cac_cau_hoi_thuong_gap_ve_Spring_Boot\" >C\u00e1c c\u00e2u h\u1ecfi th\u01b0\u1eddng g\u1eb7p v\u1ec1 Spring Boot<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-8\" href=\"https:\/\/itviec.com\/blog\/huong-dan-spring-boot-tutorial\/#Tong_ket\" >T\u1ed5ng k\u1ebft<\/a><\/li><\/ul><\/nav><\/div>\n\n<p><strong><em>Spring Framework n\u1ed5i ti\u1ebfng nh\u1edd t\u00ednh m\u1ea1nh m\u1ebd, linh ho\u1ea1t, nh\u01b0ng c\u0169ng n\u1ed5i ti\u1ebfng v\u1edbi s\u1ef1 ph\u1ee9c t\u1ea1p. \u0110\u1ec3 gi\u1ea3i quy\u1ebft b\u00e0i to\u00e1n n\u00e0y, Spring Boot \u0111\u00e3 ra \u0111\u1eddi nh\u01b0 m\u1ed9t ph\u1ea7n m\u1edf r\u1ed9ng c\u1ee7a Spring Framework, \u0111\u01b0\u1ee3c t\u1ea1o ra \u0111\u1ec3 \u0111\u01a1n gi\u1ea3n h\u00f3a qu\u00e1 tr\u00ecnh thi\u1ebft l\u1eadp v\u00e0 ph\u00e1t tri\u1ec3n \u1ee9ng d\u1ee5ng. B\u00e0i h\u01b0\u1edbng d\u1eabn Spring Boot tutorial n\u00e0y s\u1ebd ch\u1ec9 cho b\u1ea1n c\u00e1ch t\u1eadn d\u1ee5ng s\u1ee9c m\u1ea1nh c\u1ee7a Spring Boot \u0111\u1ec3 x\u00e2y d\u1ef1ng \u1ee9ng d\u1ee5ng m\u1ed9t c\u00e1ch nhanh ch\u00f3ng v\u00e0 hi\u1ec7u qu\u1ea3.&nbsp;<\/em><\/strong><\/p>\n\n\n\n<p>\u0110\u1ecdc tutorial d\u01b0\u1edbi \u0111\u00e2y \u0111\u1ec3 \u0111\u01b0\u1ee3c h\u01b0\u1edbng d\u1eabn:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>C\u00e0i \u0111\u1eb7t m\u00f4i tr\u01b0\u1eddng ph\u00e1t tri\u1ec3n ph\u00f9 h\u1ee3p<\/li>\n\n\n\n<li>Kh\u1edfi t\u1ea1o d\u1ef1 \u00e1n Spring Boot nhanh ch\u00f3ng<\/li>\n\n\n\n<li>S\u1eed d\u1ee5ng IDE \u0111\u1ec3 t\u1ea1o v\u00e0 qu\u1ea3n l\u00fd d\u1ef1 \u00e1n<\/li>\n\n\n\n<li>X\u00e2y d\u1ef1ng RESTful API v\u1edbi Spring Boot<\/li>\n\n\n\n<li>L\u00e0m vi\u1ec7c v\u1edbi c\u01a1 s\u1edf d\u1eef li\u1ec7u (JPA\/Hibernate)<\/li>\n\n\n\n<li>B\u1ea3o m\u1eadt \u1ee9ng d\u1ee5ng b\u1eb1ng Spring Security<\/li>\n\n\n\n<li>Th\u1ef1c hi\u1ec7n ki\u1ec3m th\u1eed v\u1edbi Spring Boot (Unit &amp; Integration Testing)<\/li>\n<\/ul>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p><em>\u0110\u1ecdc chi ti\u1ebft: <strong><a href=\"https:\/\/itviec.com\/blog\/spring-boot-la-gi\/\" target=\"_blank\" rel=\"noreferrer noopener\">Spring Boot l\u00e0 g\u00ec: Chi ti\u1ebft c\u00e1ch x\u00e2y d\u1ef1ng \u1ee9ng d\u1ee5ng v\u1edbi Spring Boot<\/a><\/strong><\/em><\/p>\n<\/blockquote>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-h\u01b0\u1edbng-d\u1eabn-cai-d\u1eb7t-moi-tr\u01b0\u1eddng-phat-tri\u1ec3n\"><span class=\"ez-toc-section\" id=\"Huong_dan_cai_dat_moi_truong_phat_trien\"><\/span><strong>H\u01b0\u1edbng d\u1eabn c\u00e0i \u0111\u1eb7t m\u00f4i tr\u01b0\u1eddng ph\u00e1t tri\u1ec3n<\/strong><span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>Tr\u01b0\u1edbc khi b\u1eaft \u0111\u1ea7u vi\u1ebft nh\u1eefng d\u00f2ng code Spring Boot \u0111\u1ea7u ti\u00ean, vi\u1ec7c quan tr\u1ecdng nh\u1ea5t l\u00e0 ch\u00fang ta c\u1ea7n chu\u1ea9n b\u1ecb m\u1ed9t m\u00f4i tr\u01b0\u1eddng ph\u00e1t tri\u1ec3n ho\u00e0n ch\u1ec9nh. \u0110\u1eebng lo l\u1eafng, qu\u00e1 tr\u00ecnh n\u00e0y kh\u00e1 \u0111\u01a1n gi\u1ea3n v\u00e0 m\u00ecnh s\u1ebd h\u01b0\u1edbng d\u1eabn b\u1ea1n t\u1eebng b\u01b0\u1edbc m\u1ed9t.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-cai-d\u1eb7t-java-development-kit-jdk\"><strong>C\u00e0i \u0111\u1eb7t Java Development Kit (JDK)<\/strong><\/h3>\n\n\n\n<p>JDK l\u00e0 n\u1ec1n t\u1ea3ng cho m\u1ecdi \u1ee9ng d\u1ee5ng Java, cung c\u1ea5p tr\u00ecnh bi\u00ean d\u1ecbch, th\u01b0 vi\u1ec7n v\u00e0 m\u00f4i tr\u01b0\u1eddng th\u1ef1c thi. Spring Boot 3.x y\u00eau c\u1ea7u <strong>Java 17<\/strong> tr\u1edf l\u00ean.&nbsp;<\/p>\n\n\n\n<p>\u0110\u1ec3 ki\u1ec3m tra phi\u00ean b\u1ea3n hi\u1ec7n t\u1ea1i: B\u1ea1n m\u1edf Terminal\/Command Prompt, g\u00f5 l\u1ec7nh: <code>java -version<\/code><\/p>\n\n\n\n<p>N\u1ebfu ch\u01b0a c\u00f3 ho\u1eb7c phi\u00ean b\u1ea3n kh\u00f4ng \u0111\u00fang, h\u00e3y c\u00e0i \u0111\u1eb7t JDK.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"h-h\u01b0\u1edbng-d\u1eabn-cai-d\u1eb7t-jdk\"><strong>H\u01b0\u1edbng d\u1eabn c\u00e0i \u0111\u1eb7t JDK<\/strong><\/h4>\n\n\n\n<p>B\u1ea1n c\u00f3 th\u1ec3 ch\u1ecdn gi\u1eefa hai b\u1ea3n th\u00f4ng d\u1ee5ng:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Oracle JDK:<a href=\"https:\/\/www.oracle.com\/java\/technologies\/downloads\/\" target=\"_blank\" rel=\"noreferrer noopener\"> https:\/\/www.oracle.com\/java\/technologies\/downloads\/<\/a><\/li>\n\n\n\n<li>OpenJDK (khuy\u1ebfn ngh\u1ecb): V\u00ed d\u1ee5, Adoptium Temurin t\u1ea1i<a href=\"https:\/\/adoptium.net\/\" target=\"_blank\" rel=\"noreferrer noopener\"> https:\/\/adoptium.net\/<\/a><\/li>\n<\/ul>\n\n\n\n<p>C\u00e1c b\u01b0\u1edbc c\u00e0i \u0111\u1eb7t:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>T\u1ea3i b\u1ea3n c\u00e0i \u0111\u1eb7t ph\u00f9 h\u1ee3p v\u1edbi h\u1ec7 \u0111i\u1ec1u h\u00e0nh c\u1ee7a b\u1ea1n (Windows, macOS, Linux) t\u1eeb m\u1ed9t trong c\u00e1c link tr\u00ean.<\/li>\n\n\n\n<li>Ch\u1ea1y file c\u00e0i \u0111\u1eb7t v\u00e0 l\u00e0m theo h\u01b0\u1edbng d\u1eabn.<\/li>\n\n\n\n<li>Thi\u1ebft l\u1eadp bi\u1ebfn m\u00f4i tr\u01b0\u1eddng <code>JAVA_HOME<\/code>:\n<ul class=\"wp-block-list\">\n<li>Tr\u1ecf <code>JAVA_HOME<\/code> \u0111\u1ebfn th\u01b0 m\u1ee5c c\u00e0i \u0111\u1eb7t JDK (v\u00ed d\u1ee5: <code>C:\\Program Files\\Java\\jdk-17<\/code>).<\/li>\n\n\n\n<li>Th\u00eam <code>%JAVA_HOME%\\bin<\/code> (Windows) ho\u1eb7c <code>$JAVA_HOME\/bin<\/code> (macOS\/Linux) v\u00e0o bi\u1ebfn <code>Path<\/code> c\u1ee7a h\u1ec7 th\u1ed1ng.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>M\u1edf Terminal\/Command Prompt m\u1edbi, ki\u1ec3m tra l\u1ea1i v\u1edbi <code>java -version<\/code>.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-cai-d\u1eb7t-maven-ho\u1eb7c-gradle\"><strong>C\u00e0i \u0111\u1eb7t Maven ho\u1eb7c Gradle<\/strong><\/h3>\n\n\n\n<p>C\u00e1c c\u00f4ng c\u1ee5 nh\u01b0 Apache Maven v\u00e0 Gradle \u0111\u1ec1u l\u00e0 nh\u1eefng h\u1ec7 th\u1ed1ng qu\u1ea3n l\u00fd build v\u00e0 dependency, c\u00f3 ch\u1ee9c n\u0103ng t\u1ef1 \u0111\u1ed9ng h\u00f3a qu\u00e1 tr\u00ecnh bi\u00ean d\u1ecbch, \u0111\u00f3ng g\u00f3i v\u00e0 qu\u1ea3n l\u00fd th\u01b0 vi\u1ec7n cho d\u1ef1 \u00e1n.&nbsp;<\/p>\n\n\n\n<p>Apache Maven s\u1eed d\u1ee5ng t\u1ec7p <code>pom.xml<\/code> v\u00e0 ho\u1ea1t \u0111\u1ed9ng d\u1ef1a tr\u00ean quy \u01b0\u1edbc, l\u00e0 m\u1ed9t l\u1ef1a ch\u1ecdn r\u1ea5t ph\u1ed5 bi\u1ebfn v\u00e0 quen thu\u1ed9c trong c\u1ed9ng \u0111\u1ed3ng. Trong khi \u0111\u00f3, Gradle mang \u0111\u1ebfn s\u1ef1 linh ho\u1ea1t cao h\u01a1n v\u1edbi vi\u1ec7c s\u1eed d\u1ee5ng Groovy ho\u1eb7c Kotlin DSL trong t\u1ec7p <code>build.gradle<\/code>, \u0111\u1ed3ng th\u1eddi th\u01b0\u1eddng cho t\u1ed1c \u0111\u1ed9 build nhanh h\u01a1n.&nbsp;<\/p>\n\n\n\n<p>\u0110\u1ed1i v\u1edbi ng\u01b0\u1eddi m\u1edbi b\u1eaft \u0111\u1ea7u, Maven th\u01b0\u1eddng \u0111\u01b0\u1ee3c khuy\u00ean d\u00f9ng v\u00ec t\u00ednh d\u1ec5 ti\u1ebfp c\u1eadn c\u1ee7a n\u00f3.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"h-h\u01b0\u1edbng-d\u1eabn-cai-d\u1eb7t-apache-maven\"><strong>H\u01b0\u1edbng d\u1eabn c\u00e0i \u0111\u1eb7t<\/strong> <strong>Apache Maven<\/strong><\/h4>\n\n\n\n<ul class=\"wp-block-list\">\n<li>B\u01b0\u1edbc 1: T\u1ea3i Maven: <a href=\"https:\/\/maven.apache.org\/download.cgi\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/maven.apache.org\/download.cgi<\/a><\/li>\n\n\n\n<li>B\u01b0\u1edbc 2: Gi\u1ea3i n\u00e9n v\u00e0o m\u1ed9t th\u01b0 m\u1ee5c (v\u00ed d\u1ee5: <code>C:\\Program Files\\Apache\\maven<\/code>).<\/li>\n\n\n\n<li>B\u01b0\u1edbc 3: Thi\u1ebft l\u1eadp bi\u1ebfn m\u00f4i tr\u01b0\u1eddng:\n<ul class=\"wp-block-list\">\n<li><code>M2_HOME<\/code> (ho\u1eb7c <code>MAVEN_HOME<\/code>): Tr\u1ecf \u0111\u1ebfn th\u01b0 m\u1ee5c gi\u1ea3i n\u00e9n Maven.<\/li>\n\n\n\n<li>Th\u00eam <code>%M2_HOME%\\bin<\/code> (Windows) ho\u1eb7c <code>$M2_HOME\/bin <\/code>(macOS\/Linux) v\u00e0o <code>Path<\/code>.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>Ki\u1ec3m tra: <code>mvn -version<\/code>.<\/li>\n<\/ul>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"h-h\u01b0\u1edbng-d\u1eabn-cai-d\u1eb7t-gradle\"><strong>H\u01b0\u1edbng d\u1eabn c\u00e0i \u0111\u1eb7t Gradle<\/strong><\/h4>\n\n\n\n<ul class=\"wp-block-list\">\n<li>B\u01b0\u1edbc 1: T\u1ea3i Gradle:<a href=\"https:\/\/gradle.org\/install\/\" target=\"_blank\" rel=\"noreferrer noopener\"> https:\/\/gradle.org\/install\/<\/a><\/li>\n\n\n\n<li>B\u01b0\u1edbc 2: L\u00e0m theo h\u01b0\u1edbng d\u1eabn tr\u00ean trang ch\u1ee7 ho\u1eb7c gi\u1ea3i n\u00e9n th\u1ee7 c\u00f4ng.<\/li>\n\n\n\n<li>B\u01b0\u1edbc 3: N\u1ebfu c\u00e0i manual, thi\u1ebft l\u1eadp <code>GRADLE_HOME<\/code> v\u00e0 th\u00eam <code>$GRADLE_HOME\/bin<\/code> v\u00e0o <code>Path<\/code>.<\/li>\n\n\n\n<li>Ki\u1ec3m tra: <code>gradle -version<\/code>.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-cai-d\u1eb7t-ide-integrated-development-environment\"><strong>C\u00e0i \u0111\u1eb7t IDE (Integrated Development Environment)<\/strong><\/h3>\n\n\n\n<p>IDE cung c\u1ea5p tr\u00ecnh so\u1ea1n th\u1ea3o code th\u00f4ng minh, debugger v\u00e0 c\u00e1c c\u00f4ng c\u1ee5 h\u1ed7 tr\u1ee3 ph\u00e1t tri\u1ec3n. M\u1ed9t s\u1ed1 IDE ph\u1ed5 bi\u1ebfn bao g\u1ed3m:<\/p>\n\n\n\n<p><strong>IntelliJ IDEA:<\/strong> M\u1ea1nh m\u1ebd v\u00e0 th\u00f4ng minh v\u1edbi kh\u1ea3 n\u0103ng ph\u00e2n t\u00edch v\u00e0 g\u1ee3i \u00fd code h\u00e0ng \u0111\u1ea7u..<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Community Edition (Mi\u1ec5n ph\u00ed):<\/strong> \u0110\u1ee7 d\u00f9ng cho h\u1ea7u h\u1ebft c\u00e1c d\u1ef1 \u00e1n Spring Boot.<\/li>\n\n\n\n<li><strong>Ultimate Edition (Tr\u1ea3 ph\u00ed):<\/strong> Nhi\u1ec1u t\u00ednh n\u0103ng n\u00e2ng cao h\u01a1n.<\/li>\n\n\n\n<li>Link t\u1ea3i: <a href=\"https:\/\/www.jetbrains.com\/idea\/download\/\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/www.jetbrains.com\/idea\/download\/<\/a><\/li>\n<\/ul>\n\n\n\n<p><strong>Eclipse IDE + Spring Tools Suite (STS): <\/strong>T\u00edch h\u1ee3p s\u00e2u d\u00e0nh ri\u00eang cho h\u1ec7 sinh th\u00e1i Spring gi\u00fap \u0111i\u1ec1u h\u01b0\u1edbng v\u00e0 qu\u1ea3n l\u00fd d\u1ef1 \u00e1n d\u1ec5 d\u00e0ng.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>T\u1ea3i Eclipse IDE for Java Developers: <a href=\"https:\/\/www.eclipse.org\/downloads\/packages\/\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/www.eclipse.org\/downloads\/packages\/<\/a><\/li>\n\n\n\n<li>C\u00e0i STS t\u1eeb Eclipse Marketplace (Help -&gt; Eclipse Marketplace -&gt; t\u00ecm &#8220;Spring Tools&#8221;) ho\u1eb7c t\u1ea3i b\u1ea3n t\u00edch h\u1ee3p s\u1eb5n t\u1eeb <a href=\"https:\/\/spring.io\/tools\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/spring.io\/tools<\/a>.<\/li>\n<\/ul>\n\n\n\n<p><strong>Visual Studio Code (VS Code) + Extensions: <\/strong>Nh\u1eb9, nhanh v\u00e0 linh ho\u1ea1t h\u01a1n.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>T\u1ea3i VS Code:<a href=\"https:\/\/code.visualstudio.com\/download\" target=\"_blank\" rel=\"noreferrer noopener\"> https:\/\/code.visualstudio.com\/download<\/a><\/li>\n\n\n\n<li>C\u00e0i \u0111\u1eb7t c\u00e1c extension: &#8220;Extension Pack for Java&#8221; (Microsoft) v\u00e0 &#8220;Spring Boot Extension Pack&#8221; (VMware).<\/li>\n<\/ul>\n\n\n\n<p>Sau khi ho\u00e0n th\u00e0nh c\u00e1c b\u01b0\u1edbc tr\u00ean, m\u00f4i tr\u01b0\u1eddng c\u1ee7a b\u1ea1n \u0111\u00e3 s\u1eb5n s\u00e0ng \u0111\u1ec3 b\u1eaft \u0111\u1ea7u v\u1edbi Spring Boot!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-h\u01b0\u1edbng-d\u1eabn-t\u1ea1o-d\u1ef1-an-spring-boot-d\u1ea7u-tien\"><span class=\"ez-toc-section\" id=\"Huong_dan_tao_du_an_Spring_Boot_dau_tien\"><\/span><strong>H\u01b0\u1edbng d\u1eabn t\u1ea1o d\u1ef1 \u00e1n Spring Boot \u0111\u1ea7u ti\u00ean<\/strong><span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>Sau khi \u0111\u00e3 chu\u1ea9n b\u1ecb xong m\u00f4i tr\u01b0\u1eddng, b\u1ea1n c\u00f3 th\u1ec3 t\u1ea1o d\u1ef1 \u00e1n Spring Boot \u0111\u1ea7u ti\u00ean. C\u00e1ch ph\u1ed5 bi\u1ebfn v\u00e0 d\u1ec5 d\u00e0ng nh\u1ea5t \u0111\u1ec3 l\u00e0m \u0111i\u1ec1u n\u00e0y l\u00e0 s\u1eed d\u1ee5ng <strong><a href=\"https:\/\/itviec.com\/blog\/spring-initializr-la-gi\/\" target=\"_blank\" rel=\"noreferrer noopener\">Spring Initializr<\/a><\/strong>, m\u1ed9t c\u00f4ng c\u1ee5 web ti\u1ec7n l\u1ee3i do ch\u00ednh \u0111\u1ed9i ng\u0169 Spring cung c\u1ea5p.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-t\u1ed5ng-quan-v\u1ec1-spring-initializr-start-spring-io\"><strong>T\u1ed5ng quan v\u1ec1 Spring Initializr (start.spring.io)<\/strong><\/h3>\n\n\n\n<p>Spring Initializr (<a href=\"https:\/\/start.spring.io\/\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/start.spring.io\/<\/a>) cho ph\u00e9p b\u1ea1n t\u00f9y ch\u1ec9nh v\u00e0 t\u1ea1o ra m\u1ed9t &#8220;b\u1ed9 x\u01b0\u01a1ng&#8221; d\u1ef1 \u00e1n Spring Boot v\u1edbi c\u00e1c c\u1ea5u h\u00ecnh v\u00e0 dependency c\u01a1 b\u1ea3n ch\u1ec9 trong v\u00e0i c\u00fa nh\u1ea5p chu\u1ed9t.<\/p>\n\n\n\n<p>Khi truy c\u1eadp v\u00e0o trang web, b\u1ea1n s\u1ebd th\u1ea5y m\u1ed9t giao di\u1ec7n tr\u1ef1c quan v\u1edbi c\u00e1c t\u00f9y ch\u1ecdn \u0111\u1ec3 c\u1ea5u h\u00ecnh d\u1ef1 \u00e1n c\u1ee7a m\u00ecnh nh\u01b0 h\u00ecnh d\u01b0\u1edbi \u0111\u00e2y:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"500\" src=\"https:\/\/itviec.com\/blog\/wp-content\/uploads\/2025\/07\/image-4.png\" alt=\"\" class=\"wp-image-89026\" srcset=\"https:\/\/itviec.com\/blog\/wp-content\/uploads\/2025\/07\/image-4.png 800w, https:\/\/itviec.com\/blog\/wp-content\/uploads\/2025\/07\/image-4-300x188.png 300w, https:\/\/itviec.com\/blog\/wp-content\/uploads\/2025\/07\/image-4-640x400.png 640w, https:\/\/itviec.com\/blog\/wp-content\/uploads\/2025\/07\/image-4-200x125.png 200w, https:\/\/itviec.com\/blog\/wp-content\/uploads\/2025\/07\/image-4-768x480.png 768w, https:\/\/itviec.com\/blog\/wp-content\/uploads\/2025\/07\/image-4-1536x960.png 1536w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><\/figure>\n\n\n\n<p>H\u00e3y c\u00f9ng t\u00ecm hi\u1ec3u chi ti\u1ebft t\u1eebng m\u1ee5c:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Project (D\u1ef1 \u00e1n): <\/strong>L\u1ef1a ch\u1ecdn gi\u1eefa <strong>Maven Project<\/strong> v\u00e0 <strong>Gradle Project<\/strong>. C\u1ea3 hai \u0111\u1ec1u l\u00e0 c\u00f4ng c\u1ee5 qu\u1ea3n l\u00fd build v\u00e0 dependency m\u1ea1nh m\u1ebd. <strong>Maven<\/strong> th\u01b0\u1eddng \u0111\u01b0\u1ee3c ch\u1ecdn nhi\u1ec1u h\u01a1n trong c\u00e1c h\u01b0\u1edbng d\u1eabn ban \u0111\u1ea7u v\u00ec c\u1ea5u tr\u00fac <code>pom.xml<\/code> c\u1ee7a n\u00f3 kh\u00e1 d\u1ec5 \u0111\u1ecdc.&nbsp;<em>\u1ede b\u00e0i n\u00e0y ch\u00fang ta s\u1ebd s\u1eed d\u1ee5ng Maven.<\/em><\/li>\n\n\n\n<li><strong>Language (Ng\u00f4n ng\u1eef): <\/strong>Spring Boot h\u1ed7 tr\u1ee3 Java, Kotlin, v\u00e0 Groovy. <em>\u1ede b\u00e0i n\u00e0y ch\u00fang ta <strong>ch\u1ecdn Java.<\/strong><\/em><\/li>\n\n\n\n<li><strong>Spring Boot (Phi\u00ean b\u1ea3n Spring Boot): <\/strong>M\u1ee5c n\u00e0y cho ph\u00e9p b\u1ea1n ch\u1ecdn phi\u00ean b\u1ea3n Spring Boot cho d\u1ef1 \u00e1n.&nbsp;<\/li>\n<\/ul>\n\n\n\n<p>Lu\u00f4n \u01b0u ti\u00ean ch\u1ecdn phi\u00ean b\u1ea3n <strong>\u1ed5n \u0111\u1ecbnh m\u1edbi nh\u1ea5t<\/strong> (th\u01b0\u1eddng \u0111\u01b0\u1ee3c \u0111\u00e1nh d\u1ea5u m\u1eb7c \u0111\u1ecbnh v\u00e0 kh\u00f4ng c\u00f3 ch\u1eef SNAPSHOT ho\u1eb7c M &#8211; Milestone). V\u00ed d\u1ee5: <code>3.3.0<\/code>. Tr\u00e1nh c\u00e1c phi\u00ean b\u1ea3n th\u1eed nghi\u1ec7m n\u1ebfu b\u1ea1n mu\u1ed1n s\u1ef1 \u1ed5n \u0111\u1ecbnh.<\/p>\n\n\n\n<p><strong>Project Metadata (Th\u00f4ng tin d\u1ef1 \u00e1n):<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Group (Nh\u00f3m):<\/strong> Th\u01b0\u1eddng l\u00e0 t\u00ean domain \u0111\u1ea3o ng\u01b0\u1ee3c c\u1ee7a t\u1ed5 ch\u1ee9c ho\u1eb7c c\u00e1 nh\u00e2n b\u1ea1n. V\u00ed d\u1ee5: <code>com.myblog<\/code> ho\u1eb7c <code>vn.coderschool<\/code>. N\u00f3 gi\u00fap \u0111\u1ecbnh danh duy nh\u1ea5t cho d\u1ef1 \u00e1n c\u1ee7a b\u1ea1n trong m\u1ed9t kho l\u01b0u tr\u1eef Maven.<\/li>\n\n\n\n<li><strong>Artifact (T\u00ean c\u1ea5u ph\u1ea7n):<\/strong> \u0110\u00e2y l\u00e0 t\u00ean c\u1ee7a d\u1ef1 \u00e1n c\u1ee7a b\u1ea1n (t\u00ean file <code>.jar<\/code> ho\u1eb7c <code>.war<\/code> s\u1ebd \u0111\u01b0\u1ee3c t\u1ea1o ra). V\u00ed d\u1ee5: <code>spring-boot-tutorial<\/code> ho\u1eb7c <code>my-first-app<\/code>.<\/li>\n\n\n\n<li><strong>Name (T\u00ean d\u1ef1 \u00e1n):<\/strong> Th\u01b0\u1eddng gi\u1ed1ng v\u1edbi Artifact, \u0111\u00e2y l\u00e0 t\u00ean hi\u1ec3n th\u1ecb c\u1ee7a d\u1ef1 \u00e1n.<\/li>\n\n\n\n<li><strong>Description (M\u00f4 t\u1ea3):<\/strong> M\u00f4 t\u1ea3 ng\u1eafn g\u1ecdn v\u1ec1 d\u1ef1 \u00e1n. V\u00ed d\u1ee5: Demo project for Spring Boot.<\/li>\n\n\n\n<li><strong>Package name (T\u00ean g\u00f3i):<\/strong> T\u1ef1 \u0111\u1ed9ng \u0111\u01b0\u1ee3c t\u1ea1o ra t\u1eeb Group v\u00e0 Artifact (v\u00ed d\u1ee5: com.myblog.springboot_tutorial). \u0110\u00e2y l\u00e0 package g\u1ed1c ch\u1ee9a class Application ch\u00ednh c\u1ee7a b\u1ea1n. B\u1ea1n c\u00f3 th\u1ec3 t\u00f9y ch\u1ec9nh n\u1ebfu mu\u1ed1n.<\/li>\n<\/ul>\n\n\n\n<p><strong>Packaging (\u0110\u00f3ng g\u00f3i):<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Jar:<\/strong> T\u1ea1o ra m\u1ed9t file .jar th\u1ef1c thi \u0111\u1ed9c l\u1eadp, \u0111\u00e3 nh\u00fang s\u1eb5n server (nh\u01b0 Tomcat). \u0110\u00e2y l\u00e0 l\u1ef1a ch\u1ecdn ph\u1ed5 bi\u1ebfn v\u00e0 \u0111\u01b0\u1ee3c khuy\u1ebfn ngh\u1ecb cho h\u1ea7u h\u1ebft c\u00e1c \u1ee9ng d\u1ee5ng microservices v\u00e0 web \u0111\u01a1n gi\u1ea3n. <em>\u1ede b\u00e0i n\u00e0y ch\u00fang ta s\u1ebd ch\u1ecdn Jar.<\/em><\/li>\n\n\n\n<li><strong>War:<\/strong> T\u1ea1o ra m\u1ed9t file .war \u0111\u1ec3 tri\u1ec3n khai tr\u00ean m\u1ed9t server Java EE truy\u1ec1n th\u1ed1ng (nh\u01b0 Tomcat, JBoss b\u00ean ngo\u00e0i). Th\u01b0\u1eddng \u00edt \u0111\u01b0\u1ee3c s\u1eed d\u1ee5ng h\u01a1n v\u1edbi Spring Boot tr\u1eeb khi c\u00f3 y\u00eau c\u1ea7u c\u1ee5 th\u1ec3.<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Java (Phi\u00ean b\u1ea3n Java): <\/strong>Ch\u1ecdn phi\u00ean b\u1ea3n Java b\u1ea1n \u0111\u00e3 c\u00e0i \u0111\u1eb7t v\u00e0 mu\u1ed1n s\u1eed d\u1ee5ng cho d\u1ef1 \u00e1n. V\u00ed d\u1ee5: <strong>17<\/strong> (ph\u00f9 h\u1ee3p v\u1edbi Spring Boot 3.x).<\/li>\n\n\n\n<li><strong>Dependencies (C\u00e1c th\u01b0 vi\u1ec7n ph\u1ee5 thu\u1ed9c): <\/strong>\u0110\u00e2y l\u00e0 n\u01a1i b\u1ea1n th\u00eam c\u00e1c &#8220;starter&#8221; v\u00e0 th\u01b0 vi\u1ec7n c\u1ea7n thi\u1ebft cho d\u1ef1 \u00e1n. Nh\u1ea5n v\u00e0o n\u00fat &#8220;ADD DEPENDENCIES&#8230;&#8221; ho\u1eb7c s\u1eed d\u1ee5ng \u00f4 t\u00ecm ki\u1ebfm.<\/li>\n<\/ul>\n\n\n\n<p>\u0110\u1ed1i v\u1edbi d\u1ef1 \u00e1n \u0111\u1ea7u ti\u00ean, ch\u00fang ta s\u1ebd th\u00eam m\u1ed9t v\u00e0i dependency c\u01a1 b\u1ea3n:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Spring Web:<\/strong> R\u1ea5t quan tr\u1ecdng! Dependency n\u00e0y bao g\u1ed3m m\u1ecdi th\u1ee9 c\u1ea7n thi\u1ebft \u0111\u1ec3 x\u00e2y d\u1ef1ng \u1ee9ng d\u1ee5ng web, bao g\u1ed3m RESTful API, v\u00e0 t\u1ef1 \u0111\u1ed9ng c\u1ea5u h\u00ecnh m\u1ed9t server Tomcat nh\u00fang. T\u00ecm ki\u1ebfm &#8220;Web&#8221; v\u00e0 ch\u1ecdn &#8220;Spring Web&#8221;.<\/li>\n\n\n\n<li><strong>Lombok (T\u00f9y ch\u1ecdn nh\u01b0ng r\u1ea5t h\u1eefu \u00edch):<\/strong> M\u1ed9t th\u01b0 vi\u1ec7n tuy\u1ec7t v\u1eddi gi\u00fap gi\u1ea3m thi\u1ec3u code boilerplate (code l\u1eb7p \u0111i l\u1eb7p l\u1ea1i) nh\u01b0 getters, setters, constructors, toString(), v.v. b\u1eb1ng c\u00e1ch s\u1eed d\u1ee5ng c\u00e1c annotation. T\u00ecm ki\u1ebfm &#8220;Lombok&#8221; v\u00e0 ch\u1ecdn.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-cac-b\u01b0\u1edbc-t\u1ea3i-v\u1ec1-va-import-vao-ide\"><strong>C\u00e1c b\u01b0\u1edbc t\u1ea3i v\u1ec1 v\u00e0 import v\u00e0o IDE<\/strong><\/h3>\n\n\n\n<p><strong>B\u01b0\u1edbc 1: T\u1ea1o d\u1ef1 \u00e1n (Generate project)<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Sau khi \u0111\u00e3 c\u1ea5u h\u00ecnh xong t\u1ea5t c\u1ea3 c\u00e1c t\u00f9y ch\u1ecdn, nh\u1ea5n n\u00fat <strong>&#8220;GENERATE&#8221;<\/strong> (ho\u1eb7c Ctrl + Enter).<\/li>\n\n\n\n<li>M\u1ed9t file .zip (v\u00ed d\u1ee5: spring-boot-tutorial.zip) s\u1ebd \u0111\u01b0\u1ee3c t\u1ea3i v\u1ec1 m\u00e1y t\u00ednh c\u1ee7a b\u1ea1n.<\/li>\n<\/ul>\n\n\n\n<p><strong>B\u01b0\u1edbc 2: <\/strong>Gi\u1ea3i n\u00e9n file .zip v\u1eeba t\u1ea3i v\u1ec1 v\u00e0o m\u1ed9t th\u01b0 m\u1ee5c l\u00e0m vi\u1ec7c m\u00e0 b\u1ea1n ch\u1ecdn.<\/p>\n\n\n\n<p><strong>B\u01b0\u1edbc 3: <\/strong>Import v\u00e0o IDE y\u00eau th\u00edch c\u1ee7a b\u1ea1n (IntelliJ IDEA, Eclipse v\u1edbi STS, ho\u1eb7c VS Code).<\/p>\n\n\n\n<p><strong>V\u1edbi IntelliJ IDEA:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Ch\u1ecdn &#8220;Open&#8221; \u2192 Tr\u1ecf \u0111\u1ebfn th\u01b0 m\u1ee5c b\u1ea1n v\u1eeba gi\u1ea3i n\u00e9n d\u1ef1 \u00e1n (th\u01b0 m\u1ee5c ch\u1ee9a file pom.xml) \u2192 IntelliJ IDEA s\u1ebd t\u1ef1 \u0111\u1ed9ng nh\u1eadn di\u1ec7n \u0111\u00e2y l\u00e0 m\u1ed9t d\u1ef1 \u00e1n Maven v\u00e0 ti\u1ebfn h\u00e0nh t\u1ea3i c\u00e1c dependency c\u1ea7n thi\u1ebft.<\/li>\n<\/ul>\n\n\n\n<p><strong>V\u1edbi Eclipse (\u0111\u00e3 c\u00e0i STS):&nbsp;<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Ch\u1ecdn &#8220;File&#8221; \u2192 &#8220;Import&#8230;&#8221;<\/li>\n\n\n\n<li>Trong c\u1eeda s\u1ed5 Import, t\u00ecm v\u00e0 ch\u1ecdn &#8220;Maven&#8221; \u2192 &#8220;Existing Maven Projects&#8221; \u2192 Nh\u1ea5n &#8220;Next&#8221; \u2192 Nh\u1ea5n &#8220;Browse&#8230;&#8221; v\u00e0 tr\u1ecf \u0111\u1ebfn th\u01b0 m\u1ee5c g\u1ed1c c\u1ee7a d\u1ef1 \u00e1n b\u1ea1n v\u1eeba gi\u1ea3i n\u00e9n.<\/li>\n\n\n\n<li>Eclipse s\u1ebd ph\u00e1t hi\u1ec7n file pom.xml v\u00e0 hi\u1ec3n th\u1ecb d\u1ef1 \u00e1n. \u0110\u1ea3m b\u1ea3o d\u1ef1 \u00e1n \u0111\u01b0\u1ee3c ch\u1ecdn v\u00e0 nh\u1ea5n &#8220;Finish&#8221;.<\/li>\n<\/ul>\n\n\n\n<p><strong>V\u1edbi Visual Studio Code (\u0111\u00e3 c\u00e0i Java Extension Pack v\u00e0 Spring Boot Extension Pack):<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Ch\u1ecdn &#8220;File&#8221; -&gt; &#8220;Open Folder&#8230;&#8221; \u2192 Tr\u1ecf \u0111\u1ebfn th\u01b0 m\u1ee5c d\u1ef1 \u00e1n b\u1ea1n v\u1eeba gi\u1ea3i n\u00e9n.<\/li>\n\n\n\n<li>VS Code s\u1ebd t\u1ef1 \u0111\u1ed9ng nh\u1eadn di\u1ec7n d\u1ef1 \u00e1n Java\/Maven. B\u1ea1n c\u00f3 th\u1ec3 c\u1ea7n \u0111\u1ee3i m\u1ed9t ch\u00fat \u0111\u1ec3 c\u00e1c extension kh\u1edfi t\u1ea1o v\u00e0 t\u1ea3i dependency.<\/li>\n<\/ul>\n\n\n\n<p>Sau khi import th\u00e0nh c\u00f4ng, IDE s\u1ebd b\u1eaft \u0111\u1ea7u t\u1ea3i xu\u1ed1ng c\u00e1c dependency \u0111\u00e3 khai b\u00e1o trong file pom.xml (n\u1ebfu b\u1ea1n ch\u1ecdn Maven). Qu\u00e1 tr\u00ecnh n\u00e0y c\u00f3 th\u1ec3 m\u1ea5t v\u00e0i ph\u00fat t\u00f9y thu\u1ed9c v\u00e0o t\u1ed1c \u0111\u1ed9 m\u1ea1ng c\u1ee7a b\u1ea1n.<\/p>\n\n\n\n<p>V\u1eady l\u00e0 b\u1ea1n \u0111\u00e3 t\u1ea1o th\u00e0nh c\u00f4ng d\u1ef1 \u00e1n Spring Boot \u0111\u1ea7u ti\u00ean c\u1ee7a m\u00ecnh! \u1ede ph\u1ea7n ti\u1ebfp theo, ch\u00fang ta s\u1ebd kh\u00e1m ph\u00e1 c\u1ea5u tr\u00fac c\u1ee7a d\u1ef1 \u00e1n n\u00e0y v\u00e0 vi\u1ebft nh\u1eefng d\u00f2ng code \u0111\u1ea7u ti\u00ean.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-h\u01b0\u1edbng-d\u1eabn-xay-d\u1ef1ng-restful-api-v\u1edbi-spring-boot\"><span class=\"ez-toc-section\" id=\"Huong_dan_xay_dung_RESTful_API_voi_Spring_Boot\"><\/span><strong>H\u01b0\u1edbng d\u1eabn x\u00e2y d\u1ef1ng RESTful API v\u1edbi Spring Boot<\/strong><span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>RESTful API cho ph\u00e9p c\u00e1c \u1ee9ng d\u1ee5ng t\u01b0\u01a1ng t\u00e1c v\u1edbi t\u00e0i nguy\u00ean tr\u00ean server qua HTTP, th\u01b0\u1eddng d\u00f9ng JSON \u0111\u1ec3 trao \u0111\u1ed5i d\u1eef li\u1ec7u. Spring Boot gi\u00fap vi\u1ec7c n\u00e0y tr\u1edf n\u00ean \u0111\u01a1n gi\u1ea3n h\u01a1n.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-gi\u1edbi-thi\u1ec7u-v\u1ec1-restful-api\"><strong>Gi\u1edbi thi\u1ec7u v\u1ec1 RESTful API<\/strong><\/h3>\n\n\n\n<p><a href=\"https:\/\/aws.amazon.com\/vi\/what-is\/restful-api\/\" target=\"_blank\" rel=\"noreferrer noopener\"><strong>RESTful API<\/strong> (Representational State Transfer)<\/a> l\u00e0 m\u1ed9t ki\u1ec3u ki\u1ebfn tr\u00fac ph\u1ea7n m\u1ec1m ti\u00eau chu\u1ea9n \u0111\u01b0\u1ee3c s\u1eed d\u1ee5ng \u0111\u1ec3 thi\u1ebft k\u1ebf c\u00e1c d\u1ecbch v\u1ee5 web. N\u00f3 ho\u1ea1t \u0111\u1ed9ng d\u1ef1a tr\u00ean m\u1ed9t t\u1eadp h\u1ee3p c\u00e1c nguy\u00ean t\u1eafc c\u1ed1t l\u00f5i nh\u1eb1m t\u1ea1o ra m\u1ed9t h\u1ec7 th\u1ed1ng linh ho\u1ea1t, c\u00f3 kh\u1ea3 n\u0103ng m\u1edf r\u1ed9ng v\u00e0 d\u1ec5 b\u1ea3o tr\u00ec.<\/p>\n\n\n\n<p>C\u00e1c \u0111\u1eb7c \u0111i\u1ec3m ch\u00ednh c\u1ee7a ki\u1ebfn tr\u00fac n\u00e0y bao g\u1ed3m:&nbsp;<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>M\u00f4 h\u00ecnh <strong>Client-Server<\/strong>, gi\u00fap t\u00e1ch bi\u1ec7t ho\u00e0n to\u00e0n gi\u1eefa giao di\u1ec7n ng\u01b0\u1eddi d\u00f9ng (client) v\u00e0 n\u01a1i x\u1eed l\u00fd logic, l\u01b0u tr\u1eef d\u1eef li\u1ec7u (server).&nbsp;<\/li>\n\n\n\n<li><strong>Stateless<\/strong> (phi tr\u1ea1ng th\u00e1i) y\u00eau c\u1ea7u m\u1ed7i y\u00eau c\u1ea7u t\u1eeb client ph\u1ea3i ch\u1ee9a \u0111\u1ee7 th\u00f4ng tin \u0111\u1ec3 server x\u1eed l\u00fd m\u00e0 kh\u00f4ng c\u1ea7n l\u01b0u l\u1ea1i b\u1ea5t k\u1ef3 tr\u1ea1ng th\u00e1i n\u00e0o t\u1eeb c\u00e1c phi\u00ean l\u00e0m vi\u1ec7c tr\u01b0\u1edbc.&nbsp;<\/li>\n\n\n\n<li><strong>Cacheable<\/strong> cho ph\u00e9p l\u01b0u \u0111\u1ec7m ph\u1ea3n h\u1ed3i \u0111\u1ec3 t\u0103ng hi\u1ec7u su\u1ea5t<\/li>\n\n\n\n<li>\u0110\u01b0\u1ee3c x\u00e2y d\u1ef1ng theo ki\u1ec3u <strong>Layered System<\/strong> (h\u1ec7 th\u1ed1ng ph\u00e2n l\u1edbp), gi\u00fap t\u0103ng t\u00ednh module v\u00e0 b\u1ea3o m\u1eadt<\/li>\n<\/ul>\n\n\n\n<p>Nguy\u00ean t\u1eafc trung t\u00e2m c\u1ee7a REST l\u00e0 <strong>Uniform Interface<\/strong> (giao di\u1ec7n \u0111\u1ed3ng nh\u1ea5t), t\u1ee9c l\u00e0 s\u1eed d\u1ee5ng c\u00e1c ph\u01b0\u01a1ng th\u1ee9c HTTP ti\u00eau chu\u1ea9n (GET, POST, PUT, DELETE) m\u1ed9t c\u00e1ch nh\u1ea5t qu\u00e1n \u0111\u1ec3 thao t\u00e1c tr\u00ean c\u00e1c t\u00e0i nguy\u00ean. V\u00ed d\u1ee5, \u0111\u1ec3 qu\u1ea3n l\u00fd s\u1ea3n ph\u1ea9m, m\u1ed9t RESTful API s\u1ebd c\u00f3 c\u00e1c endpoint r\u00f5 r\u00e0ng nh\u01b0 v\u00ed d\u1ee5 sau:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>GET \/api\/product<\/code>s: L\u1ea5y t\u1ea5t c\u1ea3 s\u1ea3n ph\u1ea9m.<\/li>\n\n\n\n<li><code>GET \/api\/products\/{id}<\/code>: L\u1ea5y s\u1ea3n ph\u1ea9m theo ID.<\/li>\n\n\n\n<li><code>POST \/api\/products<\/code>: T\u1ea1o s\u1ea3n ph\u1ea9m m\u1edbi.<\/li>\n\n\n\n<li><code>PUT \/api\/products\/{id}<\/code>: C\u1eadp nh\u1eadt s\u1ea3n ph\u1ea9m.<\/li>\n\n\n\n<li><code>DELETE \/api\/products\/{id}<\/code>: X\u00f3a s\u1ea3n ph\u1ea9m.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-cach-s\u1eed-d\u1ee5ng-cac-annotation-chinh-cho-rest-controller\"><strong>C\u00e1ch s\u1eed d\u1ee5ng c\u00e1c Annotation ch\u00ednh cho REST Controller<\/strong><\/h3>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"h-restcontroller\"><strong><code>@RestController<\/code><\/strong><\/h4>\n\n\n\n<p>\u0110\u00e2y l\u00e0 m\u1ed9t annotation chuy\u00ean d\u1ee5ng \u0111\u1ec3 x\u00e2y d\u1ef1ng REST API trong Spring. V\u1ec1 c\u01a1 b\u1ea3n, n\u00f3 l\u00e0 s\u1ef1 k\u1ebft h\u1ee3p c\u1ee7a <code>@Controller<\/code> v\u00e0 <code>@ResponseBody<\/code>. Khi b\u1ea1n d\u00f9ng <code>@RestController<\/code> \u0111\u1ec3 \u0111\u00e1nh d\u1ea5u m\u1ed9t class, Spring s\u1ebd t\u1ef1 \u0111\u1ed9ng coi t\u1ea5t c\u1ea3 c\u00e1c ph\u01b0\u01a1ng th\u1ee9c trong class \u0111\u00f3 nh\u01b0 th\u1ec3 ch\u00fang \u0111\u1ec1u c\u00f3 <code>@ResponseBody<\/code>.<\/p>\n\n\n\n<p>K\u1ebft qu\u1ea3 l\u00e0 m\u1ecdi d\u1eef li\u1ec7u b\u1ea1n tr\u1ea3 v\u1ec1 t\u1eeb ph\u01b0\u01a1ng th\u1ee9c (nh\u01b0 Object, List) s\u1ebd \u0111\u01b0\u1ee3c chuy\u1ec3n \u0111\u1ed5i th\u1eb3ng th\u00e0nh JSON v\u00e0 g\u1eedi v\u1ec1 trong response body m\u00e0 kh\u00f4ng c\u1ea7n th\u00eam b\u1ea5t k\u1ef3 annotation n\u00e0o kh\u00e1c.<\/p>\n\n\n\n<p>V\u00ed d\u1ee5:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>package com.example.demo.controller;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.bind.annotation.GetMapping;\n\n@RestController\npublic class HelloController {\n    @GetMapping(\"\/hello-rest\")\n    public String sayHello() { return \"Hello from REST!\"; }\n}<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"h-requestmapping-nbsp\"><strong><code>@RequestMapping<\/code><\/strong>&nbsp;<\/h4>\n\n\n\n<p>\u0110\u00e2y l\u00e0 annotation \u0111\u01b0\u1ee3c d\u00f9ng \u0111\u1ec3 \u00e1nh x\u1ea1 m\u1ed9t URL c\u1ee7a request \u0111\u1ebfn ph\u01b0\u01a1ng th\u1ee9c x\u1eed l\u00fd t\u01b0\u01a1ng \u1ee9ng trong controller. N\u00f3 r\u1ea5t linh ho\u1ea1t, cho ph\u00e9p b\u1ea1n \u0111\u1eb7t \u1edf c\u1ea5p class \u0111\u1ec3 \u0111\u1ecbnh ngh\u0129a m\u1ed9t ti\u1ec1n t\u1ed1 URL chung cho c\u1ea3 nh\u00f3m endpoint, v\u00ed d\u1ee5 <code>@RequestMapping(\"\/api\/users\")<\/code>. Sau \u0111\u00f3, b\u1ea1n c\u00f3 th\u1ec3 ti\u1ebfp t\u1ee5c s\u1eed d\u1ee5ng n\u00f3 \u1edf c\u1ea5p ph\u01b0\u01a1ng th\u1ee9c \u0111\u1ec3 x\u00e1c \u0111\u1ecbnh \u0111\u01b0\u1eddng d\u1eabn c\u1ee5 th\u1ec3, v\u00ed d\u1ee5 <code>(\"\/{id}\")<\/code>, v\u00e0 \u0111\u01b0\u1eddng d\u1eabn cu\u1ed1i c\u00f9ng \u0111\u1ec3 truy c\u1eadp s\u1ebd l\u00e0 s\u1ef1 k\u1ebft h\u1ee3p c\u1ee7a c\u1ea3 hai.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>C\u00e1c annotation r\u00fat g\u1ecdn:<\/strong> bao g\u1ed3m <code>@GetMapping<\/code>, <code>@PostMapping<\/code>, <code>@PutMapping<\/code>, <code>@DeleteMapping<\/code>, <code>@PatchMapping<\/code>.<\/li>\n<\/ul>\n\n\n\n<p>V\u00ed d\u1ee5:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>package com.example.demo.controller;\nimport org.springframework.web.bind.annotation.*;\n\n@RestController\n@RequestMapping(\"\/api\/v1\/products\") \/\/ Ti\u1ec1n t\u1ed1 chung\npublic class ProductController {\n    @GetMapping\n    public String getAllProducts() { return \"List of products\"; }\n\n    @PostMapping\n    public String createProduct() { return \"Product created\"; }\n}<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"h-cac-annotation-trich-xu\u1ea5t-d\u1eef-li\u1ec7u-t\u1eeb-request\"><strong>C\u00e1c annotation tr\u00edch xu\u1ea5t d\u1eef li\u1ec7u t\u1eeb Request<\/strong><\/h4>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>@PathVariable<\/strong>: L\u1ea5y gi\u00e1 tr\u1ecb t\u1eeb URI path.&nbsp;<\/li>\n<\/ul>\n\n\n\n<p>V\u00ed d\u1ee5: <code>\/products\/{id}<\/code>.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@GetMapping(\"\/{productId}\")\npublic String getProductById(@PathVariable Long productId) {\n    return \"Product ID: \" + productId;\n}<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>@RequestParam<\/strong>: L\u1ea5y gi\u00e1 tr\u1ecb t\u1eeb query parameters.&nbsp;<\/li>\n<\/ul>\n\n\n\n<p>V\u00ed d\u1ee5: <code>\/search?query=laptop<\/code>.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@GetMapping(\"\/search\")\npublic String search(@RequestParam String query) {\n    return \"Searching for: \" + query;\n}<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong><code>@RequestBody<\/code><\/strong>: L\u1ea5y to\u00e0n b\u1ed9 request body (th\u01b0\u1eddng l\u00e0 JSON) v\u00e0 chuy\u1ec3n th\u00e0nh \u0111\u1ed1i t\u01b0\u1ee3ng Java. D\u00f9ng cho <code>POST<\/code>, <code>PUT<\/code>.<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ Gi\u1ea3 s\u1eed c\u00f3 class ProductDTO { private String name; \/* getters\/setters *\/ }\n@PostMapping(\"\/dto\")\npublic ProductDTO createWithDto(@RequestBody ProductDTO productPayload) {\n    System.out.println(\"Received: \" + productPayload.getName());\n    return productPayload;\n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-h\u01b0\u1edbng-d\u1eabn-x\u1eed-ly-request-va-response\"><strong>H\u01b0\u1edbng d\u1eabn x\u1eed l\u00fd Request v\u00e0 Response<\/strong><\/h3>\n\n\n\n<p>Trong Spring Boot, vi\u1ec7c x\u1eed l\u00fd d\u1eef li\u1ec7u <strong>request<\/strong> v\u00e0 <strong>response<\/strong> r\u1ea5t \u0111\u01a1n gi\u1ea3n.&nbsp;<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Nh\u1eadn d\u1eef li\u1ec7u request:<\/strong><\/li>\n<\/ul>\n\n\n\n<p>\u0110\u1ec3 nh\u1eadn d\u1eef li\u1ec7u, b\u1ea1n d\u00f9ng annotation <code>@RequestBody<\/code> \u0111\u1ec3 t\u1ef1 \u0111\u1ed9ng chuy\u1ec3n \u0111\u1ed5i m\u1ed9t chu\u1ed7i JSON trong request th\u00e0nh \u0111\u1ed1i t\u01b0\u1ee3ng Java. \u0110\u1ed1i v\u1edbi d\u1eef li\u1ec7u t\u1eeb form, Spring c\u00f3 th\u1ec3 t\u1ef1 \u0111\u1ed9ng li\u00ean k\u1ebft (bind) ch\u00fang v\u00e0o c\u00e1c tham s\u1ed1 ph\u01b0\u01a1ng th\u1ee9c.&nbsp;<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Tr\u1ea3 d\u1eef li\u1ec7u:<\/strong><\/li>\n<\/ul>\n\n\n\n<p>\u1ede chi\u1ec1u ng\u01b0\u1ee3c l\u1ea1i, khi s\u1eed d\u1ee5ng <code>@RestController<\/code>, b\u1ea1n ch\u1ec9 c\u1ea7n tr\u1ea3 v\u1ec1 m\u1ed9t \u0111\u1ed1i t\u01b0\u1ee3ng Java b\u1ea5t k\u1ef3, v\u00e0 n\u00f3 s\u1ebd \u0111\u01b0\u1ee3c t\u1ef1 \u0111\u1ed9ng chuy\u1ec3n \u0111\u1ed5i th\u00e0nh JSON trong ph\u1ea7n th\u00e2n c\u1ee7a HTTP response \u0111\u1ec3 g\u1eedi v\u1ec1 cho client.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>M\u00e3 tr\u1ea1ng th\u00e1i HTTP:<\/strong><\/li>\n<\/ul>\n\n\n\n<p>Spring Boot t\u1ef1 \u0111\u1ed9ng g\u00e1n c\u00e1c m\u00e3 tr\u1ea1ng th\u00e1i HTTP h\u1ee3p l\u00fd nh\u01b0 <strong>200 OK<\/strong> cho c\u00e1c y\u00eau c\u1ea7u th\u00e0nh c\u00f4ng, <strong>201 Created<\/strong> khi t\u1ea1o m\u1edbi qua POST, v\u00e0 <strong>204 No Content <\/strong>sau khi DELETE th\u00e0nh c\u00f4ng.<\/p>\n\n\n\n<p>Tuy nhi\u00ean, \u0111\u1ec3 c\u00f3 to\u00e0n quy\u1ec1n ki\u1ec3m so\u00e1t v\u00e0 x\u1eed l\u00fd c\u00e1c tr\u01b0\u1eddng h\u1ee3p ph\u1ee9c t\u1ea1p h\u01a1n nh\u01b0 tr\u1ea3 v\u1ec1 l\u1ed7i 404 ho\u1eb7c th\u00eam headers, b\u1ea1n n\u00ean s\u1eed d\u1ee5ng <strong>ResponseEntity&lt;T&gt;<\/strong>. L\u1edbp n\u00e0y cho ph\u00e9p b\u1ea1n t\u00f9y ch\u1ec9nh \u0111\u1ed3ng th\u1eddi c\u1ea3 ph\u1ea7n th\u00e2n (body), c\u00e1c headers, v\u00e0 m\u00e3 tr\u1ea1ng th\u00e1i c\u1ee7a response m\u1ed9t c\u00e1ch t\u01b0\u1eddng minh.<\/p>\n\n\n\n<p>V\u00ed d\u1ee5:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import org.springframework.http.HttpStatus;\nimport org.springframework.http.ResponseEntity;\n\/\/ Gi\u1ea3 s\u1eed c\u00f3 class Product { Long id; String name; ... }\n\n@GetMapping(\"\/items\/{id}\")\npublic ResponseEntity&lt;Product&gt; getItem(@PathVariable Long id) {\n    Product product = findProductById(id); \/\/ H\u00e0m gi\u1ea3 \u0111\u1ecbnh\n    if (product != null) {\n        return ResponseEntity.ok(product); \/\/ 200 OK\n    } else {\n        return ResponseEntity.notFound().build(); \/\/ 404 Not Found\n    }\n}\n\n@PostMapping(\"\/items\")\npublic ResponseEntity&lt;Product&gt; createItem(@RequestBody Product product) {\n    Product createdProduct = saveProduct(product); \/\/ H\u00e0m gi\u1ea3 \u0111\u1ecbnh\n    return new ResponseEntity&lt;&gt;(createdProduct, HttpStatus.CREATED); \/\/ 201 Created\n}\n\n\/\/ C\u00e1c h\u00e0m v\u00e0 class gi\u1ea3 \u0111\u1ecbnh\nprivate Product findProductById(Long id) {\n    if (id == 1L) return new Product(id, \"Sample Product\"); return null;\n}\nprivate Product saveProduct(Product p) { p.setId(System.currentTimeMillis()); return p; }\nstatic class Product {\n    private Long id; private String name;\n    public Product(Long id, String name) { this.id = id; this.name = name; }\n    public Long getId() { return id; } public void setId(Long id) { this.id = id; }\n    public String getName() { return name;} public void setName(String name) { this.name = name;}\n}<\/code><\/pre>\n\n\n\n<p>Ho\u1eb7c b\u1ea1n c\u00f3 th\u1ec3 d\u00f9ng <code>@ResponseStatus<\/code> tr\u00ean ph\u01b0\u01a1ng th\u1ee9c handler ho\u1eb7c class Exception.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-hi\u1ec3u-v\u1ec1-data-transfer-objects-dtos\"><strong>Hi\u1ec3u v\u1ec1 Data Transfer Objects (DTOs)<\/strong><\/h3>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"h-t\u1ea1i-sao-c\u1ea7n-dto\"><strong>T\u1ea1i sao c\u1ea7n DTO?<\/strong><\/h4>\n\n\n\n<p>Data Transfer Object (DTO) l\u00e0 m\u1ed9t \u0111\u1ed1i t\u01b0\u1ee3ng \u0111\u01a1n gi\u1ea3n d\u00f9ng \u0111\u1ec3 mang d\u1eef li\u1ec7u gi\u1eefa c\u00e1c t\u1ea7ng kh\u00e1c nhau c\u1ee7a \u1ee9ng d\u1ee5ng, \u0111\u1eb7c bi\u1ec7t l\u00e0 cho c\u00e1c API.<\/p>\n\n\n\n<p>S\u1eed d\u1ee5ng DTO l\u00e0 m\u1ed9t th\u1ef1c h\u00e0nh t\u1ed1t nh\u1ea5t (best practice) v\u00ec n\u00f3 t\u00e1ch bi\u1ec7t c\u1ea5u tr\u00fac d\u1eef li\u1ec7u c\u00f4ng khai c\u1ee7a API kh\u1ecfi c\u1ea5u tr\u00fac entity n\u1ed9i b\u1ed9 trong c\u01a1 s\u1edf d\u1eef li\u1ec7u. \u0110i\u1ec1u n\u00e0y cho ph\u00e9p b\u1ea1n ki\u1ec3m so\u00e1t ch\u00ednh x\u00e1c d\u1eef li\u1ec7u \u0111\u01b0\u1ee3c ph\u01a1i b\u00e0y ra ngo\u00e0i, gi\u00fap tr\u00e1nh l\u00e0m l\u1ed9 th\u00f4ng tin nh\u1ea1y c\u1ea3m. DTO c\u0169ng l\u00e0 n\u01a1i l\u00fd t\u01b0\u1edfng \u0111\u1ec3 \u00e1p d\u1ee5ng c\u00e1c quy t\u1eafc validation (x\u00e1c th\u1ef1c) cho d\u1eef li\u1ec7u \u0111\u1ea7u v\u00e0o.<\/p>\n\n\n\n<p>Ngo\u00e0i ra, \u0111\u1ed1i v\u1edbi c\u00e1c \u1ee9ng d\u1ee5ng d\u00f9ng JPA, m\u1eabu thi\u1ebft k\u1ebf n\u00e0y c\u00f2n gi\u1ea3i quy\u1ebft tri\u1ec7t \u0111\u1ec3 l\u1ed7i <code>LazyInitializationException<\/code> ph\u1ed5 bi\u1ebfn b\u1eb1ng c\u00e1ch t\u1ea1o ra m\u1ed9t \u0111\u1ed1i t\u01b0\u1ee3ng ch\u1ee9a d\u1eef li\u1ec7u \u0111\u1ed9c l\u1eadp tr\u01b0\u1edbc khi phi\u00ean giao d\u1ecbch v\u1edbi c\u01a1 s\u1edf d\u1eef li\u1ec7u \u0111\u00f3ng l\u1ea1i.<\/p>\n\n\n\n<p><strong>V\u00ed d\u1ee5:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ DTO cho t\u1ea1o user\n\/\/ package com.example.demo.dto;\npublic class UserCreationDTO {\n    private String username;\n    private String password;\n    private String email;\n    \/\/ Getters &amp; Setters\n    public String getUsername() { return username; }\n    public void setUsername(String u) { username = u; }\n    public String getPassword() { return password; }\n    public void setPassword(String p) { password = p; }\n    public String getEmail() { return email; }\n    public void setEmail(String e) { email = e; }\n}\n\n\/\/ DTO cho response user\n\/\/ package com.example.demo.dto;\npublic class UserResponseDTO {\n    private Long id;\n    private String username;\n    private String email;\n    \/\/ Getters &amp; Setters\n    public Long getId() { return id; }\n    public void setId(Long i) { id = i; }\n    public String getUsername() { return username; }\n    public void setUsername(String u) { username = u; }\n    public String getEmail() { return email; }\n    public void setEmail(String e) { email = e; }\n}<\/code><\/pre>\n\n\n\n<p>S\u1eed d\u1ee5ng trong Controller, b\u1ea1n s\u1ebd c\u1ea7n logic \u0111\u1ec3 chuy\u1ec3n \u0111\u1ed5i gi\u1eefa DTO v\u00e0 Entity (th\u1ee7 c\u00f4ng ho\u1eb7c d\u00f9ng th\u01b0 vi\u1ec7n nh\u01b0 ModelMapper).<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-h\u01b0\u1edbng-d\u1eabn-s\u1eed-d\u1ee5ng-validation-xac-th\u1ef1c-d\u1eef-li\u1ec7u\"><strong>H\u01b0\u1edbng d\u1eabn s\u1eed d\u1ee5ng Validation (X\u00e1c th\u1ef1c d\u1eef li\u1ec7u)<\/strong><\/h3>\n\n\n\n<p>\u0110\u1ec3 x\u00e1c th\u1ef1c d\u1eef li\u1ec7u trong Spring Boot, b\u1ea1n ch\u1ec9 c\u1ea7n th\u00eam c\u00e1c annotation t\u1eeb Bean Validation API (nh\u01b0 <strong>@NotNull, @Size, @Email<\/strong>) tr\u1ef1c ti\u1ebfp l\u00ean c\u00e1c tr\u01b0\u1eddng c\u1ee7a l\u1edbp DTO.&nbsp;<\/p>\n\n\n\n<p>V\u00ed d\u1ee5:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ package com.example.demo.dto;\nimport jakarta.validation.constraints.Email;\nimport jakarta.validation.constraints.NotBlank;\nimport jakarta.validation.constraints.Size;\npublic class ArticleCreationDTO {\n    @NotBlank @Size(min = 5, max = 100) private String title;\n    @NotBlank private String content;\n    @Email private String authorEmail;\n    \/\/ Getters &amp; Setters\n    public String getTitle() { return title; } public void setTitle(String t) { title = t; }\n    public String getContent() { return content; } public void setContent(String c) { content = c; }\n    public String getAuthorEmail() { return authorEmail; } public void setAuthorEmail(String e) { authorEmail = e; }\n}<\/code><\/pre>\n\n\n\n<p>Sau \u0111\u00f3, \u0111\u1ec3 k\u00edch ho\u1ea1t qu\u00e1 tr\u00ecnh x\u00e1c th\u1ef1c khi nh\u1eadn request, h\u00e3y \u0111\u1eb7t annotation <strong>@Valid <\/strong>ngay tr\u01b0\u1edbc tham s\u1ed1 DTO trong ph\u01b0\u01a1ng th\u1ee9c controller c\u1ee7a b\u1ea1n.&nbsp;<\/p>\n\n\n\n<p>V\u00ed d\u1ee5 th\u00eam <code>@Valid<\/code> (t\u1eeb jakarta.validation.Valid) tr\u01b0\u1edbc tham s\u1ed1 <code>@RequestBody<\/code> trong controller:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ import com.example.demo.dto.ArticleCreationDTO;\n\/\/ import jakarta.validation.Valid;\n@PostMapping(\"\/articles-validated\")\npublic ResponseEntity&lt;String&gt; createArticleValidated(@Valid @RequestBody ArticleCreationDTO articleDTO) {\n    return ResponseEntity.ok(\"Article valid: \" + articleDTO.getTitle());\n}<\/code><\/pre>\n\n\n\n<p>N\u1ebfu c\u00f3 l\u1ed7i validation, Spring s\u1ebd t\u1ef1 \u0111\u1ed9ng n\u00e9m <code>MethodArgumentNotValidException<\/code> (tr\u1ea3 v\u1ec1 <strong>response 400 Bad Request)<\/strong>. Dependency cho t\u00ednh n\u0103ng n\u00e0y (<strong>spring-boot-starter-validation<\/strong>) th\u01b0\u1eddng \u0111\u00e3 \u0111\u01b0\u1ee3c t\u00edch h\u1ee3p s\u1eb5n khi b\u1ea1n d\u00f9ng <strong>spring-boot-starter-web<\/strong>. B\u1ea1n c\u00f3 th\u1ec3 t\u00f9y ch\u1ec9nh x\u1eed l\u00fd l\u1ed7i n\u00e0y.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-h\u01b0\u1edbng-d\u1eabn-x\u1eed-ly-ngo\u1ea1i-l\u1ec7-exception-handling\"><strong>H\u01b0\u1edbng d\u1eabn x\u1eed l\u00fd ngo\u1ea1i l\u1ec7 (Exception Handling)<\/strong><\/h3>\n\n\n\n<p>\u0110\u1ec3 x\u1eed l\u00fd ngo\u1ea1i l\u1ec7 (exception) m\u1ed9t c\u00e1ch t\u1eadp trung trong Spring Boot, b\u1ea1n s\u1eed d\u1ee5ng k\u1ebft h\u1ee3p hai annotation <strong>@ControllerAdvice<\/strong> v\u00e0 <strong>@ExceptionHandler<\/strong>.&nbsp;<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u0110\u1ea7u ti\u00ean, b\u1ea1n t\u1ea1o m\u1ed9t class chung v\u00e0 \u0111\u00e1nh d\u1ea5u n\u00f3 v\u1edbi <strong>@ControllerAdvice<\/strong> (ho\u1eb7c <strong>@RestControllerAdvice <\/strong>cho REST API) \u0111\u1ec3 bi\u1ebfn n\u00f3 th\u00e0nh m\u1ed9t b\u1ed9 x\u1eed l\u00fd ngo\u1ea1i l\u1ec7 to\u00e0n c\u1ee5c.&nbsp;<\/li>\n\n\n\n<li>B\u00ean trong class n\u00e0y, b\u1ea1n t\u1ea1o c\u00e1c ph\u01b0\u01a1ng th\u1ee9c \u0111\u01b0\u1ee3c \u0111\u00e1nh d\u1ea5u v\u1edbi <strong>@ExceptionHandler,<\/strong> trong \u0111\u00f3 m\u1ed7i ph\u01b0\u01a1ng th\u1ee9c ch\u1ec9 \u0111\u1ecbnh m\u1ed9t lo\u1ea1i exception c\u1ee5 th\u1ec3 m\u00e0 n\u00f3 s\u1ebd b\u1eaft v\u00e0 x\u1eed l\u00fd (v\u00ed d\u1ee5: ResourceNotFoundException.class).&nbsp;<\/li>\n\n\n\n<li>Khi m\u1ed9t exception t\u01b0\u01a1ng \u1ee9ng \u0111\u01b0\u1ee3c n\u00e9m ra t\u1eeb b\u1ea5t k\u1ef3 controller n\u00e0o, ph\u01b0\u01a1ng th\u1ee9c x\u1eed l\u00fd n\u00e0y s\u1ebd t\u1ef1 \u0111\u1ed9ng \u0111\u01b0\u1ee3c g\u1ecdi \u0111\u1ec3 tr\u1ea3 v\u1ec1 m\u1ed9t response l\u1ed7i c\u00f3 c\u1ea5u tr\u00fac v\u00e0 \u0111\u01b0\u1ee3c ki\u1ec3m so\u00e1t.<\/li>\n<\/ul>\n\n\n\n<p>V\u00ed d\u1ee5:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>package com.example.demo.exception;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.web.bind.MethodArgumentNotValidException;\nimport org.springframework.web.bind.annotation.ControllerAdvice;\nimport org.springframework.web.bind.annotation.ExceptionHandler;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.stream.Collectors;\n\n@ControllerAdvice\npublic class GlobalExceptionHandler {\n\n    @ExceptionHandler(MethodArgumentNotValidException.class)\n    public ResponseEntity&lt;Object&gt; handleValidationExceptions(MethodArgumentNotValidException ex) {\n        Map&lt;String, Object&gt; body = new HashMap&lt;&gt;();\n        body.put(\"timestamp\", new Date());\n        body.put(\"status\", HttpStatus.BAD_REQUEST.value());\n        Map&lt;String, String&gt; errors = ex.getBindingResult().getFieldErrors().stream()\n            .collect(Collectors.toMap(fe -&gt; fe.getField(), fe -&gt; fe.getDefaultMessage() != null ? fe.getDefaultMessage() : \"Invalid\"));\n        body.put(\"errors\", errors);\n        return new ResponseEntity&lt;&gt;(body, HttpStatus.BAD_REQUEST);\n    }\n\n    @ExceptionHandler(ResourceNotFoundException.class) \/\/ Custom exception\n    public ResponseEntity&lt;Object&gt; handleResourceNotFound(ResourceNotFoundException ex) {\n        Map&lt;String, Object&gt; body = new HashMap&lt;&gt;();\n        body.put(\"timestamp\", new Date());\n        body.put(\"status\", HttpStatus.NOT_FOUND.value());\n        body.put(\"message\", ex.getMessage());\n        return new ResponseEntity&lt;&gt;(body, HttpStatus.NOT_FOUND);\n    }\n    \/\/ C\u00f3 th\u1ec3 th\u00eam handler cho Exception.class \u0111\u1ec3 b\u1eaft t\u1ea5t c\u1ea3 l\u1ed7i kh\u00e1c\n}\n\n\/\/ Custom Exception\n\/\/ package com.example.demo.exception;\npublic class ResourceNotFoundException extends RuntimeException {\n    public ResourceNotFoundException(String message) { super(message); }\n}<\/code><\/pre>\n\n\n\n<p>Ngo\u00e0i ra, b\u1ea1n c\u00f3 th\u1ec3 t\u1ea1o custom exception k\u1ebf th\u1eeba t\u1eeb <code>RuntimeException<\/code> ho\u1eb7c <code>Exception<\/code> \u0111\u1ec3 x\u1eed l\u00fd c\u00e1c t\u00ecnh hu\u1ed1ng l\u1ed7i c\u1ee5 th\u1ec3.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-h\u01b0\u1edbng-d\u1eabn-lam-vi\u1ec7c-v\u1edbi-c\u01a1-s\u1edf-d\u1eef-li\u1ec7u\"><span class=\"ez-toc-section\" id=\"Huong_dan_lam_viec_voi_co_so_du_lieu\"><\/span><strong>H\u01b0\u1edbng d\u1eabn l\u00e0m vi\u1ec7c v\u1edbi c\u01a1 s\u1edf d\u1eef li\u1ec7u<\/strong><span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>H\u1ea7u h\u1ebft \u1ee9ng d\u1ee5ng \u0111\u1ec1u c\u1ea7n l\u01b0u tr\u1eef d\u1eef li\u1ec7u. Spring Boot c\u00f9ng Spring Data JPA gi\u00fap b\u1ea1n l\u00e0m vi\u1ec7c v\u1edbi c\u01a1 s\u1edf d\u1eef li\u1ec7u quan h\u1ec7 m\u1ed9t c\u00e1ch d\u1ec5 d\u00e0ng. H\u00e3y th\u1ef1c h\u00e0nh v\u1edbi c\u00e1c b\u01b0\u1edbc sau \u0111\u00e2y:<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-b\u01b0\u1edbc-1-hi\u1ec3u-v\u1ec1-spring-data-jpa\"><strong>B\u01b0\u1edbc 1: Hi\u1ec3u v\u1ec1 Spring Data JPA<\/strong><\/h3>\n\n\n\n<p>Spring Data JPA l\u00e0 m\u1ed9t framework gi\u00fap \u0111\u01a1n gi\u1ea3n h\u00f3a vi\u1ec7c t\u01b0\u01a1ng t\u00e1c v\u1edbi c\u01a1 s\u1edf d\u1eef li\u1ec7u, ho\u1ea1t \u0111\u1ed9ng d\u1ef1a tr\u00ean ti\u00eau chu\u1ea9n JPA (Java Persistence API) v\u00e0 c\u00e1c c\u00f4ng c\u1ee5 ORM (Object-Relational Mapping) nh\u01b0 Hibernate.<\/p>\n\n\n\n<p>L\u1ee3i \u00edch l\u1edbn nh\u1ea5t c\u1ee7a n\u00f3 l\u00e0 gi\u1ea3m thi\u1ec3u code l\u1eb7p l\u1ea1i b\u1eb1ng c\u00e1ch:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>T\u1ef1 \u0111\u1ed9ng cung c\u1ea5p \u0111\u1ea7y \u0111\u1ee7 c\u00e1c ph\u01b0\u01a1ng th\u1ee9c CRUD th\u00f4ng qua interface k\u1ebf th\u1eeba t\u1eeb <code>JpaRepository<\/code>.<\/li>\n\n\n\n<li>Cho ph\u00e9p truy v\u1ea5n d\u1eef li\u1ec7u m\u1ed9t c\u00e1ch linh ho\u1ea1t ho\u1eb7c t\u1ef1 \u0111\u1ed9ng sinh truy v\u1ea5n t\u1eeb t\u00ean ph\u01b0\u01a1ng th\u1ee9c (Query Methods), ho\u1eb7c vi\u1ebft c\u00e1c truy v\u1ea5n ph\u1ee9c t\u1ea1p t\u00f9y ch\u1ec9nh b\u1eb1ng annotation <code>@Query<\/code> (s\u1eed d\u1ee5ng JPQL ho\u1eb7c SQL).&nbsp;<\/li>\n<\/ul>\n\n\n\n<p>Framework n\u00e0y t\u00edch h\u1ee3p ho\u00e0n h\u1ea3o v\u00e0o h\u1ec7 sinh th\u00e1i Spring, gi\u00fap qu\u00e1 tr\u00ecnh ph\u00e1t tri\u1ec3n tr\u1edf n\u00ean nhanh ch\u00f3ng v\u00e0 nh\u1ea5t qu\u00e1n.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-b\u01b0\u1edbc-2-c\u1ea5u-hinh-k\u1ebft-n\u1ed1i-c\u01a1-s\u1edf-d\u1eef-li\u1ec7u\"><strong>B\u01b0\u1edbc 2: C\u1ea5u h\u00ecnh k\u1ebft n\u1ed1i C\u01a1 s\u1edf d\u1eef li\u1ec7u<\/strong><\/h3>\n\n\n\n<p>\u0110\u1ec3 Spring Boot c\u00f3 th\u1ec3 k\u1ebft n\u1ed1i v\u1edbi m\u1ed9t c\u01a1 s\u1edf d\u1eef li\u1ec7u quan h\u1ec7, b\u1ea1n c\u1ea7n th\u1ef1c hi\u1ec7n 2 vi\u1ec7c ch\u00ednh:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Th\u00eam Dependencies (<code>pom.xml<\/code>)<\/strong><\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;dependency&gt;\n    &lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt;\n    &lt;artifactId&gt;spring-boot-starter-data-jpa&lt;\/artifactId&gt;\n&lt;\/dependency&gt;\n\n&lt;dependency&gt;\n    &lt;groupId&gt;com.h2database&lt;\/groupId&gt;\n    &lt;artifactId&gt;h2&lt;\/artifactId&gt;\n    &lt;scope&gt;runtime&lt;\/scope&gt;\n&lt;\/dependency&gt;\n\n&lt;!--\n&lt;dependency&gt;\n    &lt;groupId&gt;mysql&lt;\/groupId&gt;\n    &lt;artifactId&gt;mysql-connector-java&lt;\/artifactId&gt;\n    &lt;scope&gt;runtime&lt;\/scope&gt;\n&lt;\/dependency&gt;\n--&gt;<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>C\u1ea5u h\u00ecnh trong <code>application.properties<\/code><\/strong><\/li>\n<\/ul>\n\n\n\n<p><strong>V\u1edbi H2 (In-memory):<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># H2 Configuration\nspring.datasource.url=jdbc:h2:mem:testdb\nspring.datasource.driverClassName=org.h2.Driver\nspring.datasource.username=sa\nspring.datasource.password=\nspring.jpa.database-platform=org.hibernate.dialect.H2Dialect\nspring.jpa.hibernate.ddl-auto=update # (update, create-drop)\nspring.jpa.show-sql=true\nspring.h2.console.enabled=true\nspring.h2.console.path=\/h2-console<\/code><\/pre>\n\n\n\n<p><strong>V\u1edbi MySQL (V\u00ed d\u1ee5 Production):<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># MySQL Configuration\nspring.datasource.url=jdbc:mysql:\/\/localhost:3306\/your_db?useSSL=false&amp;serverTimezone=UTC\nspring.datasource.username=your_user\nspring.datasource.password=your_pass\nspring.datasource.driverClassName=com.mysql.cj.jdbc.Driver\nspring.jpa.database-platform=org.hibernate.dialect.MySQLDialect\nspring.jpa.hibernate.ddl-auto=validate # (validate, none - an to\u00e0n cho production)<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-gi\u1ea3i-thich-v\u1ec1-spring-jpa-hibernate-ddl-auto\"><strong>Gi\u1ea3i th\u00edch v\u1ec1 <\/strong><strong>spring.jpa.hibernate.ddl-auto<\/strong><\/h3>\n\n\n\n<p>spring.jpa.hibernate.ddl-auto l\u00e0 m\u1ed9t thu\u1ed9c t\u00ednh c\u1ea5u h\u00ecnh trong Spring Boot (s\u1eed d\u1ee5ng Hibernate l\u00e0m JPA provider) gi\u00fap b\u1ea1n ki\u1ec3m so\u00e1t c\u00e1ch Hibernate t\u01b0\u01a1ng t\u00e1c v\u1edbi c\u01a1 s\u1edf d\u1eef li\u1ec7u (CSDL) c\u1ee7a b\u1ea1n khi \u1ee9ng d\u1ee5ng kh\u1edfi \u0111\u1ed9ng. N\u00f3 t\u1ef1 \u0111\u1ed9ng th\u1ef1c hi\u1ec7n c\u00e1c thao t\u00e1c DDL (Data Definition Language) nh\u01b0 t\u1ea1o, c\u1eadp nh\u1eadt ho\u1eb7c ki\u1ec3m tra c\u1ea5u tr\u00fac b\u1ea3ng.<\/p>\n\n\n\n<p>D\u01b0\u1edbi \u0111\u00e2y l\u00e0 b\u1ea3ng gi\u1ea3i th\u00edch chi ti\u1ebft c\u00e1c gi\u00e1 tr\u1ecb th\u01b0\u1eddng d\u00f9ng:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td>T\u00f9y ch\u1ecdn&nbsp;<\/td><td>M\u00f4 t\u1ea3 chi ti\u1ebft<\/td><td>M\u00f4i tr\u01b0\u1eddng khuy\u1ebfn ngh\u1ecb&nbsp;<\/td><td>Ghi ch\u00fa&nbsp;<\/td><\/tr><tr><td>create<\/td><td>Hibernate s\u1ebd <strong>x\u00f3a to\u00e0n b\u1ed9 schema CSDL hi\u1ec7n c\u00f3<\/strong> (n\u1ebfu c\u00f3) v\u00e0 <strong>t\u1ea1o l\u1ea1i t\u1ea5t c\u1ea3 c\u00e1c b\u1ea3ng<\/strong> d\u1ef1a tr\u00ean c\u00e1c th\u1ef1c th\u1ec3 JPA m\u1ed7i khi \u1ee9ng d\u1ee5ng kh\u1edfi \u0111\u1ed9ng.<\/td><td><strong>Ph\u00e1t tri\u1ec3n (dev)<\/strong>, <strong>Test<\/strong><\/td><td>\u0110\u1ea3m b\u1ea3o CSDL lu\u00f4n &#8220;s\u1ea1ch&#8221; v\u00e0 kh\u1edbp v\u1edbi m\u00f4 h\u00ecnh d\u1eef li\u1ec7u. R\u1ea5t ti\u1ec7n l\u1ee3i cho giai \u0111o\u1ea1n ph\u00e1t tri\u1ec3n ban \u0111\u1ea7u khi b\u1ea1n th\u01b0\u1eddng xuy\u00ean thay \u0111\u1ed5i c\u1ea5u tr\u00fac d\u1eef li\u1ec7u.<\/td><\/tr><tr><td>create-drop<\/td><td>T\u01b0\u01a1ng t\u1ef1 create, Hibernate s\u1ebd x\u00f3a v\u00e0 t\u1ea1o l\u1ea1i schema khi \u1ee9ng d\u1ee5ng kh\u1edfi \u0111\u1ed9ng. \u0110i\u1ec3m kh\u00e1c bi\u1ec7t l\u00e0 khi \u1ee9ng d\u1ee5ng <strong>d\u1eebng (shutdown)<\/strong>, Hibernate s\u1ebd <strong>x\u00f3a to\u00e0n b\u1ed9 schema CSDL<\/strong>.<\/td><td>Test<\/td><td>C\u1ef1c k\u1ef3 h\u1eefu \u00edch cho c\u00e1c b\u00e0i <strong>ki\u1ec3m th\u1eed t\u00edch h\u1ee3p<\/strong> t\u1ef1 \u0111\u1ed9ng, \u0111\u1ea3m b\u1ea3o m\u1ed7i l\u1ea7n ch\u1ea1y ki\u1ec3m th\u1eed b\u1eaft \u0111\u1ea7u v\u1edbi m\u1ed9t CSDL tr\u1ed1ng r\u1ed7ng v\u00e0 nh\u1ea5t qu\u00e1n.<\/td><\/tr><tr><td>update<\/td><td>Hibernate s\u1ebd <strong>ki\u1ec3m tra s\u1ef1 kh\u00e1c bi\u1ec7t<\/strong> gi\u1eefa schema CSDL hi\u1ec7n c\u00f3 v\u00e0 m\u00f4 h\u00ecnh d\u1eef li\u1ec7u c\u1ee7a b\u1ea1n, sau \u0111\u00f3 c\u1ed1 g\u1eafng <strong>c\u1eadp nh\u1eadt schema CSDL hi\u1ec7n c\u00f3<\/strong> \u0111\u1ec3 kh\u1edbp v\u1edbi m\u00f4 h\u00ecnh d\u1eef li\u1ec7u m\u1edbi.<\/td><td>Ph\u00e1t tri\u1ec3n (dev)<\/td><td>C\u00f3 th\u1ec3 ti\u1ec7n l\u1ee3i trong dev \u0111\u1ec3 nhanh ch\u00f3ng xem c\u00e1c thay \u0111\u1ed5i schema. <strong>Tuy\u1ec7t \u0111\u1ed1i KH\u00d4NG n\u00ean s\u1eed d\u1ee5ng trong m\u00f4i tr\u01b0\u1eddng s\u1ea3n ph\u1ea9m (production)<\/strong> v\u00ec ti\u1ec1m \u1ea9n r\u1ee7i ro h\u1ecfng CSDL v\u00e0 m\u1ea5t d\u1eef li\u1ec7u n\u1ebfu Hibernate kh\u00f4ng th\u1ec3 x\u1eed l\u00fd c\u00e1c thay \u0111\u1ed5i ph\u1ee9c t\u1ea1p m\u1ed9t c\u00e1ch ch\u00ednh x\u00e1c.<\/td><\/tr><tr><td>validate<\/td><td>Hibernate s\u1ebd <strong>ki\u1ec3m tra xem schema CSDL hi\u1ec7n c\u00f3 c\u00f3 kh\u1edbp ho\u00e0n to\u00e0n<\/strong> v\u1edbi m\u00f4 h\u00ecnh d\u1eef li\u1ec7u c\u1ee7a b\u1ea1n hay kh\u00f4ng. N\u1ebfu c\u00f3 b\u1ea5t k\u1ef3 s\u1ef1 kh\u00f4ng kh\u1edbp n\u00e0o, Hibernate s\u1ebd <strong>n\u00e9m ra m\u1ed9t ngo\u1ea1i l\u1ec7<\/strong> v\u00e0 \u1ee9ng d\u1ee5ng s\u1ebd kh\u00f4ng kh\u1edfi \u0111\u1ed9ng \u0111\u01b0\u1ee3c.<\/td><td><strong>Staging<\/strong>, <strong>Production<\/strong><\/td><td>R\u1ea5t t\u1ed1t \u0111\u1ec3 \u0111\u1ea3m b\u1ea3o t\u00ednh nh\u1ea5t qu\u00e1n gi\u1eefa code v\u00e0 CSDL. Gi\u00fap ph\u00e1t hi\u1ec7n s\u1edbm c\u00e1c v\u1ea5n \u0111\u1ec1 v\u1ec1 schema tr\u01b0\u1edbc khi ch\u00fang g\u00e2y ra l\u1ed7i trong m\u00f4i tr\u01b0\u1eddng th\u1ef1c t\u1ebf.<\/td><\/tr><tr><td>none<\/td><td>Hibernate s\u1ebd <strong>kh\u00f4ng th\u1ef1c hi\u1ec7n b\u1ea5t k\u1ef3 thao t\u00e1c DDL n\u00e0o<\/strong> l\u00ean CSDL khi \u1ee9ng d\u1ee5ng kh\u1edfi \u0111\u1ed9ng.<\/td><td>Production<\/td><td><strong>An to\u00e0n nh\u1ea5t cho m\u00f4i tr\u01b0\u1eddng s\u1ea3n ph\u1ea9m.<\/strong> Khi s\u1eed d\u1ee5ng none, b\u1ea1n c\u1ea7n t\u1ef1 qu\u1ea3n l\u00fd c\u00e1c thay \u0111\u1ed5i schema CSDL. Ph\u01b0\u01a1ng ph\u00e1p t\u1ed1t nh\u1ea5t l\u00e0 s\u1eed d\u1ee5ng c\u00e1c c\u00f4ng c\u1ee5 qu\u1ea3n l\u00fd di chuy\u1ec3n CSDL chuy\u00ean nghi\u1ec7p nh\u01b0 <strong>Flyway<\/strong> ho\u1eb7c <strong>Liquibase<\/strong> \u0111\u1ec3 \u0111\u1ea3m b\u1ea3o qu\u00e1 tr\u00ecnh tri\u1ec3n khai CSDL an to\u00e0n, c\u00f3 ki\u1ec3m so\u00e1t v\u00e0 c\u00f3 kh\u1ea3 n\u0103ng rollback.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-b\u01b0\u1edbc-3-t\u1ea1o-entities-th\u1ef1c-th\u1ec3\"><strong>B\u01b0\u1edbc 3: T\u1ea1o Entities (Th\u1ef1c th\u1ec3)<\/strong><\/h3>\n\n\n\n<p>Entity l\u00e0 l\u1edbp Java \u0111\u1ea1i di\u1ec7n cho m\u1ed9t b\u1ea3ng trong database.<\/p>\n\n\n\n<p><strong>C\u00e1c JPA Annotation ch\u00ednh:<\/strong><\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td><strong>Annotation<\/strong><\/td><td><strong>Chi ti\u1ebft&nbsp;<\/strong><\/td><\/tr><tr><td>Entity&nbsp;<\/td><td>M\u1ed7i \u0111\u1ed1i t\u01b0\u1ee3ng c\u1ee7a l\u1edbp Entity s\u1ebd t\u01b0\u01a1ng \u1ee9ng v\u1edbi m\u1ed9t h\u00e0ng d\u1eef li\u1ec7u trong b\u1ea3ng \u0111\u00f3. C\u00e1c thu\u1ed9c t\u00ednh (fields) c\u1ee7a l\u1edbp Entity s\u1ebd t\u01b0\u01a1ng \u1ee9ng v\u1edbi c\u00e1c c\u1ed9t trong b\u1ea3ng.<\/td><\/tr><tr><td>@Entity<\/td><td>\u0110\u00e1nh d\u1ea5u m\u1ed9t l\u1edbp Java l\u00e0 m\u1ed9t JPA entity. B\u1eaft bu\u1ed9c ph\u1ea3i c\u00f3 \u0111\u1ec3 JPA nh\u1eadn di\u1ec7n v\u00e0 qu\u1ea3n l\u00fd l\u1edbp n\u00e0y nh\u01b0 m\u1ed9t th\u1ef1c th\u1ec3 c\u01a1 s\u1edf d\u1eef li\u1ec7u.<\/td><\/tr><tr><td>@Table(name = &#8220;ten_bang&#8221;)<\/td><td>T\u00f9y ch\u1ecdn. N\u1ebfu kh\u00f4ng khai b\u00e1o, JPA s\u1ebd m\u1eb7c \u0111\u1ecbnh l\u1ea5y t\u00ean l\u1edbp Entity l\u00e0m t\u00ean b\u1ea3ng.<br>V\u00ed d\u1ee5: <code>@Table(name = \"products\")<\/code><\/td><\/tr><tr><td>@Id<\/td><td>Thu\u1ed9c t\u00ednh \u0111\u01b0\u1ee3c \u0111\u00e1nh d\u1ea5u @Id s\u1ebd l\u00e0 duy nh\u1ea5t v\u00e0 d\u00f9ng \u0111\u1ec3 x\u00e1c \u0111\u1ecbnh m\u1ed9t h\u00e0ng d\u1eef li\u1ec7u trong b\u1ea3ng.<\/td><\/tr><tr><td>@GeneratedValue(strategy = GenerationType.IDENTITY)<\/td><td>GenerationType.IDENTITY: C\u01a1 s\u1edf d\u1eef li\u1ec7u s\u1ebd t\u1ef1 \u0111\u1ed9ng t\u1ea1o gi\u00e1 tr\u1ecb cho kh\u00f3a ch\u00ednh (th\u01b0\u1eddng d\u00f9ng cho c\u00e1c CSDL nh\u01b0 MySQL, PostgreSQL). C\u00f3 c\u00e1c chi\u1ebfn l\u01b0\u1ee3c kh\u00e1c nh\u01b0 <code>AUTO<\/code>, <code>SEQUENCE<\/code>, <code>TABLE<\/code>.<\/td><\/tr><tr><td>@Column(name = &#8220;ten_cot&#8221;)<\/td><td>T\u00f9y ch\u1ecdn. N\u1ebfu kh\u00f4ng khai b\u00e1o, JPA s\u1ebd m\u1eb7c \u0111\u1ecbnh l\u1ea5y t\u00ean thu\u1ed9c t\u00ednh l\u00e0m t\u00ean c\u1ed9t.<br>C\u00f3 th\u1ec3 c\u1ea5u h\u00ecnh th\u00eam c\u00e1c thu\u1ed9c t\u00ednh kh\u00e1c nh\u01b0 nullable, length, unique&#8230; V\u00ed d\u1ee5: <code>@Column(name = \"product_name\", nullable = false, length = 255)<\/code><\/td><\/tr><tr><td>Relationships<\/td><td>C\u00e1c annotations ph\u1ed5 bi\u1ebfn bao g\u1ed3m:<br>&#8211; @OneToMany: M\u1ed9t \u0111\u1ed1i t\u01b0\u1ee3ng c\u1ee7a Entity n\u00e0y c\u00f3 th\u1ec3 c\u00f3 nhi\u1ec1u \u0111\u1ed1i t\u01b0\u1ee3ng c\u1ee7a Entity kh\u00e1c. <br>&#8211; @ManyToOne: Nhi\u1ec1u \u0111\u1ed1i t\u01b0\u1ee3ng c\u1ee7a Entity n\u00e0y c\u00f3 th\u1ec3 li\u00ean quan \u0111\u1ebfn m\u1ed9t \u0111\u1ed1i t\u01b0\u1ee3ng c\u1ee7a Entity kh\u00e1c.<br>&#8211; @OneToOne: M\u1ed1i quan h\u1ec7 m\u1ed9t &#8211; m\u1ed9t. <br>&#8211; @ManyToMany: M\u1ed1i quan h\u1ec7 nhi\u1ec1u &#8211; nhi\u1ec1u.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>V\u00ed d\u1ee5 entity <strong>Product<\/strong>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>package com.example.demo.model;\n\nimport jakarta.persistence.*;\n\n@Entity\n@Table(name = \"products\")\npublic class Product {\n    @Id\n    @GeneratedValue(strategy = GenerationType.IDENTITY)\n    private Long id;\n\n    @Column(nullable = false)\n    private String name;\n    private Double price;\n    private int stockQuantity;\n\n    \/\/ Constructors, Getters, Setters (Lombok c\u00f3 th\u1ec3 gi\u00fap r\u00fat g\u1ecdn)\n    public Product() {}\n    public Product(String name, Double price, int stockQuantity) {\n        this.name = name; this.price = price; this.stockQuantity = stockQuantity;\n    }\n    \/\/ Getters v\u00e0 Setters cho c\u00e1c tr\u01b0\u1eddng...\n    public Long getId() { return id; }\n    public void setId(Long id) { this.id = id; }\n    public String getName() { return name; }\n    public void setName(String name) { this.name = name; }\n    public Double getPrice() { return price; }\n    public void setPrice(Double price) { this.price = price; }\n    public int getStockQuantity() { return stockQuantity; }\n    public void setStockQuantity(int stockQuantity) { this.stockQuantity = stockQuantity; }\n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-b\u01b0\u1edbc-4-t\u1ea1o-repositories\"><strong>B\u01b0\u1edbc 4: T\u1ea1o Repositories<\/strong><\/h3>\n\n\n\n<p>Trong Spring Data JPA, repository l\u00e0 m\u1ed9t interface b\u1ea1n \u0111\u1ecbnh ngh\u0129a \u0111\u1ec3 t\u01b0\u01a1ng t\u00e1c v\u1edbi c\u01a1 s\u1edf d\u1eef li\u1ec7u cho m\u1ed9t entity c\u1ee5 th\u1ec3, v\u00e0 Spring s\u1ebd t\u1ef1 \u0111\u1ed9ng t\u1ea1o l\u1edbp tri\u1ec3n khai cho n\u00f3.&nbsp;<\/p>\n\n\n\n<p>B\u1ea1n ch\u1ec9 c\u1ea7n t\u1ea1o m\u1ed9t interface k\u1ebf th\u1eeba t\u1eeb JpaRepository&lt;T, ID&gt;, v\u1edbi:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>T l\u00e0 ki\u1ec3u Entity<\/li>\n\n\n\n<li>ID l\u00e0 ki\u1ec3u c\u1ee7a kh\u00f3a ch\u00ednh.<\/li>\n<\/ul>\n\n\n\n<p>Ngay l\u1eadp t\u1ee9c, b\u1ea1n s\u1ebd c\u00f3 s\u1eb5n \u0111\u1ea7y \u0111\u1ee7 c\u00e1c ph\u01b0\u01a1ng th\u1ee9c CRUD nh\u01b0 save(), findById(), findAll().<\/p>\n\n\n\n<p>\u0110\u1ed1i v\u1edbi c\u00e1c truy v\u1ea5n t\u00f9y ch\u1ec9nh, b\u1ea1n c\u00f3 th\u1ec3 ch\u1ec9 c\u1ea7n \u0111\u1eb7t t\u00ean ph\u01b0\u01a1ng th\u1ee9c theo quy \u01b0\u1edbc \u0111\u1ec3 Spring t\u1ef1 sinh ra c\u00e2u l\u1ec7nh (v\u00ed d\u1ee5: <code>findByName(...)<\/code>), ho\u1eb7c s\u1eed d\u1ee5ng annotation <strong>@Query <\/strong>\u0111\u1ec3 vi\u1ebft c\u00e1c truy v\u1ea5n ph\u1ee9c t\u1ea1p b\u1eb1ng JPQL hay SQL.<\/p>\n\n\n\n<p><strong>V\u00ed d\u1ee5 <code>ProductRepository<\/code>:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>package com.example.demo.repository;\n\nimport com.example.demo.model.Product;\nimport org.springframework.data.jpa.repository.JpaRepository;\nimport org.springframework.stereotype.Repository;\nimport java.util.List;\n\n@Repository\npublic interface ProductRepository extends JpaRepository&lt;Product, Long&gt; {\n    List&lt;Product&gt; findByNameContainingIgnoreCase(String keyword);\n    \/\/ @Query(\"SELECT p FROM Product p WHERE p.price &lt; :maxPrice\")\n    \/\/ List&lt;Product&gt; findProductsCheaperThan(@Param(\"maxPrice\") Double maxPrice);\n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-vi-d\u1ee5-crud-d\u01a1n-gi\u1ea3n\"><strong>V\u00ed d\u1ee5 CRUD \u0111\u01a1n gi\u1ea3n<\/strong><\/h3>\n\n\n\n<p>\u0110\u1ec3 h\u00ecnh dung d\u1ec5 h\u01a1n, ch\u00fang ta s\u1ebd t\u00ecm hi\u1ec3u qua v\u00ed d\u1ee5 sau trong ng\u1eef c\u1ea3nh v\u1ec1 m\u1ed9t \u1ee9ng d\u1ee5ng qu\u1ea3n l\u00fd s\u1ea3n ph\u1ea9m&nbsp;<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>T\u1ea1o Enity: L\u1edbp <strong>Product<\/strong> \u00e1nh x\u1ea1 v\u1edbi b\u1ea3ng products trong database.<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ Product.java\nimport jakarta.persistence.*; \/\/ Ho\u1eb7c javax.persistence cho Spring Boot c\u0169 h\u01a1n\n\n@Entity\n@Table(name = \"products\")\npublic class Product {\n    @Id\n    @GeneratedValue(strategy = GenerationType.IDENTITY)\n    private Long id;\n    private String name;\n    private Double price;\n    \/\/ Getters, Setters, Constructors (IDE c\u00f3 th\u1ec3 t\u1ef1 t\u1ea1o)\n}<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Repository<\/strong> (Truy c\u1eadp d\u1eef li\u1ec7u): Interface n\u00e0y cho ph\u00e9p Spring Data JPA t\u1ef1 \u0111\u1ed9ng t\u1ea1o c\u00e1c ph\u01b0\u01a1ng th\u1ee9c t\u01b0\u01a1ng t\u00e1c v\u1edbi DB.<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ ProductRepository.java\nimport org.springframework.data.jpa.repository.JpaRepository;\nimport org.springframework.stereotype.Repository;\n\n@Repository\npublic interface ProductRepository extends JpaRepository&lt;Product, Long&gt; {\n    \/\/ T\u1ef1 \u0111\u1ed9ng c\u00f3: save(), findById(), findAll(), deleteById(), ...\n}<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Controller (API)<\/strong><\/li>\n<\/ul>\n\n\n\n<p>Endpoint m\u00e0 client g\u1ecdi t\u1edbi \u0111\u1ec3 th\u1ef1c hi\u1ec7n CRUD. N\u00f3 s\u1ebd d\u00f9ng ProductRepository tr\u1ef1c ti\u1ebfp \u0111\u1ec3 \u0111\u01a1n gi\u1ea3n h\u00f3a v\u00ed d\u1ee5.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ ProductController.java\nimport com.example.demo.model.Product; \/\/ \u0110\u1ea3m b\u1ea3o \u0111\u00fang package c\u1ee7a Product\nimport com.example.demo.repository.ProductRepository; \/\/ \u0110\u1ea3m b\u1ea3o \u0111\u00fang package c\u1ee7a Repository\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.List;\n\n@RestController\n@RequestMapping(\"\/api\/products\")\npublic class ProductController {\n\n    @Autowired\n    private ProductRepository productRepository; \/\/ Controller g\u1ecdi tr\u1ef1c ti\u1ebfp Repository \u0111\u1ec3 r\u00fat g\u1ecdn\n\n    \/\/ GET: L\u1ea5y t\u1ea5t c\u1ea3 s\u1ea3n ph\u1ea9m\n    @GetMapping\n    public List&lt;Product&gt; getAllProducts() {\n        return productRepository.findAll();\n    }\n\n    \/\/ GET: L\u1ea5y s\u1ea3n ph\u1ea9m theo ID\n    @GetMapping(\"\/{id}\")\n    public ResponseEntity&lt;Product&gt; getProductById(@PathVariable Long id) {\n        return productRepository.findById(id)\n                .map(product -&gt; new ResponseEntity&lt;&gt;(product, HttpStatus.OK))\n                .orElse(new ResponseEntity&lt;&gt;(HttpStatus.NOT_FOUND));\n    }\n\n    \/\/ POST: T\u1ea1o s\u1ea3n ph\u1ea9m m\u1edbi\n    @PostMapping\n    public ResponseEntity&lt;Product&gt; createProduct(@RequestBody Product product) {\n        Product savedProduct = productRepository.save(product);\n        return new ResponseEntity&lt;&gt;(savedProduct, HttpStatus.CREATED);\n    }\n\n    \/\/ PUT: C\u1eadp nh\u1eadt s\u1ea3n ph\u1ea9m\n    @PutMapping(\"\/{id}\")\n    public ResponseEntity&lt;Product&gt; updateProduct(@PathVariable Long id, @RequestBody Product product) {\n        if (!productRepository.existsById(id)) {\n            return new ResponseEntity&lt;&gt;(HttpStatus.NOT_FOUND);\n        }\n        product.setId(id); \/\/ \u0110\u1ea3m b\u1ea3o c\u1eadp nh\u1eadt \u0111\u00fang s\u1ea3n ph\u1ea9m\n        Product updatedProduct = productRepository.save(product);\n        return new ResponseEntity&lt;&gt;(updatedProduct, HttpStatus.OK);\n    }\n\n    \/\/ DELETE: X\u00f3a s\u1ea3n ph\u1ea9m\n    @DeleteMapping(\"\/{id}\")\n    public ResponseEntity&lt;HttpStatus&gt; deleteProduct(@PathVariable Long id) {\n        if (!productRepository.existsById(id)) {\n            return new ResponseEntity&lt;&gt;(HttpStatus.NOT_FOUND);\n        }\n        productRepository.deleteById(id);\n        return new ResponseEntity&lt;&gt;(HttpStatus.NO_CONTENT);\n    }\n}<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li>V\u00e0 cu\u1ed1i c\u00f9ng l\u00e0 c\u1ea5u h\u00ecnh database (v\u00ed d\u1ee5 H2 trong b\u1ed9 nh\u1edb):<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code>spring.h2.console.enabled=true<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>spring.h2.console.path=\/h2-console<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>spring.datasource.url=jdbc:h2:mem:testdb<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>spring.datasource.driverClassName=org.h2.Driver<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>spring.datasource.username=sa<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>spring.datasource.password=<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>spring.jpa.hibernate.ddl-auto=update<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>spring.jpa.show-sql=true<\/code><\/pre>\n\n\n\n<p><strong>C\u00e1ch ho\u1ea1t \u0111\u1ed9ng:<\/strong><\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>B\u1ea1n \u0111\u1ecbnh ngh\u0129a Product.<\/li>\n\n\n\n<li>Spring Data JPA t\u1ef1 \u0111\u1ed9ng t\u1ea1o code cho ProductRepository d\u1ef1a tr\u00ean JpaRepository.<\/li>\n\n\n\n<li>ProductController s\u1eed d\u1ee5ng ProductRepository \u0111\u1ec3 th\u1ef1c hi\u1ec7n c\u00e1c thao t\u00e1c Create, Read, Update, Delete khi nh\u1eadn request HTTP.<\/li>\n<\/ol>\n\n\n\n<p>V\u00ed d\u1ee5 n\u00e0y b\u1ecf qua l\u1edbp Service \u0111\u1ec3 l\u00e0m cho n\u00f3 ng\u1eafn g\u1ecdn nh\u1ea5t c\u00f3 th\u1ec3, m\u1eb7c d\u00f9 trong \u1ee9ng d\u1ee5ng th\u1ef1c t\u1ebf, Service Layer th\u01b0\u1eddng \u0111\u01b0\u1ee3c khuy\u1ebfn ngh\u1ecb \u0111\u1ec3 t\u00e1ch bi\u1ec7t logic nghi\u1ec7p v\u1ee5 kh\u1ecfi t\u1ea7ng API v\u00e0 d\u1eef li\u1ec7u.<\/p>\n\n\n\n<p><strong>V\u00ed d\u1ee5 <code>ProductService<\/code>:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>package com.example.demo.service;\n\nimport com.example.demo.model.Product;\nimport com.example.demo.repository.ProductRepository;\nimport com.example.demo.exception.ResourceNotFoundException; \/\/ Custom exception\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport java.util.List;\n\n@Service\npublic class ProductService {\n    @Autowired private ProductRepository productRepository;\n\n    @Transactional\n    public Product createProduct(Product product) { return productRepository.save(product); }\n\n    public List&lt;Product&gt; getAllProducts() { return productRepository.findAll(); }\n\n    public Product getProductById(Long id) {\n        return productRepository.findById(id)\n            .orElseThrow(() -&gt; new ResourceNotFoundException(\"Product\", \"id\", id));\n    }\n\n    @Transactional\n    public Product updateProduct(Long id, Product productDetails) {\n        Product existingProduct = getProductById(id); \/\/ S\u1ebd n\u00e9m l\u1ed7i n\u1ebfu kh\u00f4ng t\u00ecm th\u1ea5y\n        existingProduct.setName(productDetails.getName());\n        existingProduct.setPrice(productDetails.getPrice());\n        existingProduct.setStockQuantity(productDetails.getStockQuantity());\n        return productRepository.save(existingProduct);\n    }\n\n    @Transactional\n    public void deleteProduct(Long id) {\n        if (!productRepository.existsById(id)) {\n            throw new ResourceNotFoundException(\"Product\", \"id\", id);\n        }\n        productRepository.deleteById(id);\n    }\n}<\/code><\/pre>\n\n\n\n<p><strong>Controller s\u1eed d\u1ee5ng Service (v\u00ed d\u1ee5 r\u00fat g\u1ecdn):<\/strong>&nbsp;<\/p>\n\n\n\n<p>Controller s\u1ebd inject <code>ProductService<\/code> v\u00e0 g\u1ecdi c\u00e1c ph\u01b0\u01a1ng th\u1ee9c c\u1ee7a n\u00f3.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>package com.example.demo.controller;\n\nimport com.example.demo.model.Product; \/\/ Ho\u1eb7c DTO\nimport com.example.demo.service.ProductService;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.web.bind.annotation.*;\nimport java.util.List;\n\n@RestController\n@RequestMapping(\"\/api\/v1\/products\")\npublic class ProductController {\n    @Autowired private ProductService productService;\n\n    @PostMapping\n    public ResponseEntity&lt;Product&gt; createProduct(@RequestBody Product product) { \/\/ N\u00ean d\u00f9ng DTO\n        return new ResponseEntity&lt;&gt;(productService.createProduct(product), HttpStatus.CREATED);\n    }\n\n    @GetMapping(\"\/{id}\")\n    public ResponseEntity&lt;Product&gt; getProductById(@PathVariable Long id) { \/\/ N\u00ean d\u00f9ng DTO\n        return ResponseEntity.ok(productService.getProductById(id));\n    }\n\n    \/\/ Th\u00eam c\u00e1c endpoint kh\u00e1c cho GET all, PUT, DELETE...\n}<\/code><\/pre>\n\n\n\n<p><em>L\u01b0u \u00fd:<\/em> Trong th\u1ef1c t\u1ebf, n\u00ean s\u1eed d\u1ee5ng DTO (Data Transfer Objects) gi\u1eefa Controller v\u00e0 Client thay v\u00ec Entity tr\u1ef1c ti\u1ebfp.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-h\u01b0\u1edbng-d\u1eabn-b\u1ea3o-m\u1eadt-\u1ee9ng-d\u1ee5ng-v\u1edbi-spring-security-nbsp\"><span class=\"ez-toc-section\" id=\"Huong_dan_bao_mat_ung_dung_voi_Spring_Security\"><\/span><strong>H\u01b0\u1edbng d\u1eabn b\u1ea3o m\u1eadt \u1ee9ng d\u1ee5ng v\u1edbi Spring Security&nbsp;<\/strong><span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>Spring Security l\u00e0 m\u1ed9t framework chuy\u00ean v\u1ec1 b\u1ea3o m\u1eadt, t\u1eadp trung v\u00e0o hai nhi\u1ec7m v\u1ee5 ch\u00ednh:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>&nbsp;<strong>Authentication<\/strong> (X\u00e1c th\u1ef1c): x\u00e1c minh danh t\u00ednh c\u1ee7a ng\u01b0\u1eddi d\u00f9ng<\/li>\n\n\n\n<li>&nbsp;<strong>Authorization<\/strong> (\u1ee6y quy\u1ec1n): quy\u1ebft \u0111\u1ecbnh nh\u1eefng quy\u1ec1n truy c\u1eadp m\u00e0 ng\u01b0\u1eddi d\u00f9ng \u0111\u00f3 \u0111\u01b0\u1ee3c ph\u00e9p th\u1ef1c hi\u1ec7n sau khi \u0111\u00e3 x\u00e1c th\u1ef1c th\u00e0nh c\u00f4ng.&nbsp;<\/li>\n<\/ul>\n\n\n\n<p>V\u1ec1 m\u1eb7t k\u1ef9 thu\u1eadt, Spring Security ho\u1ea1t \u0111\u1ed9ng d\u1ef1a tr\u00ean m\u1ed9t c\u01a1 ch\u1ebf c\u1ed1t l\u00f5i l\u00e0 chu\u1ed7i c\u00e1c b\u1ed9 l\u1ecdc (filter chain), n\u01a1i m\u1ed7i y\u00eau c\u1ea7u g\u1eedi \u0111\u1ebfn \u1ee9ng d\u1ee5ng \u0111\u1ec1u \u0111\u01b0\u1ee3c x\u1eed l\u00fd qua chu\u1ed7i n\u00e0y \u0111\u1ec3 \u00e1p d\u1ee5ng c\u00e1c quy t\u1eafc b\u1ea3o m\u1eadt.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-b\u01b0\u1edbc-1-c\u1ea5u-hinh-c\u01a1-b\u1ea3n\"><strong>B\u01b0\u1edbc 1: C\u1ea5u h\u00ecnh c\u01a1 b\u1ea3n<\/strong><\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Th\u00eam dependency:<\/strong> <code>spring-boot-starter-security<\/code> v\u00e0o <code>pom.xml<\/code>.<\/li>\n<\/ol>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;dependency&gt;\n    &lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt;\n    &lt;artifactId&gt;spring-boot-starter-security&lt;\/artifactId&gt;\n&lt;\/dependency&gt;<\/code><\/pre>\n\n\n\n<p>C\u1ea5u h\u00ecnh m\u1eb7c \u0111\u1ecbnh l\u00e0:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>T\u1ea5t c\u1ea3 endpoint \u0111\u01b0\u1ee3c b\u1ea3o v\u1ec7.<\/li>\n\n\n\n<li>K\u00edch ho\u1ea1t HTTP Basic v\u00e0 Form Login.<\/li>\n\n\n\n<li>T\u1ea1o user <code>user<\/code> v\u1edbi password ng\u1eabu nhi\u00ean (in ra console).<\/li>\n\n\n\n<li>B\u1eadt CSRF protection.<\/li>\n<\/ul>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>T\u00f9y ch\u1ec9nh v\u1edbi <code>SecurityFilterChain<\/code> (Spring Boot 3+):<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"wp-block-code\"><code>package com.example.demo.config;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;\nimport org.springframework.security.crypto.password.PasswordEncoder;\nimport org.springframework.security.web.SecurityFilterChain;\nimport static org.springframework.security.config.Customizer.withDefaults;\n\n@Configuration\n@EnableWebSecurity\npublic class SecurityConfig {\n\n    @Bean\n    public PasswordEncoder passwordEncoder() {\n        return new BCryptPasswordEncoder();\n    }\n\n    @Bean\n    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n        http\n            .authorizeHttpRequests(auth -&gt; auth\n                .requestMatchers(\"\/public\/**\", \"\/h2-console\/**\").permitAll()\n                .requestMatchers(\"\/api\/admin\/**\").hasRole(\"ADMIN\")\n                .anyRequest().authenticated()\n            )\n            .formLogin(withDefaults()) \/\/ Ho\u1eb7c .formLogin(form -&gt; form.loginPage(\"\/custom-login\").permitAll())\n            .httpBasic(withDefaults())\n            .csrf(csrf -&gt; csrf.ignoringRequestMatchers(\"\/h2-console\/**\")); \/\/ T\u1eaft CSRF cho H2 console n\u1ebfu d\u00f9ng\n        return http.build();\n    }\n    \/\/ UserDetailsService bean s\u1ebd \u0111\u01b0\u1ee3c \u0111\u1ecbnh ngh\u0129a \u1edf ph\u1ea7n sau\n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-b\u01b0\u1edbc-2-authentication-xac-th\u1ef1c\"><strong>B\u01b0\u1edbc 2: Authentication (X\u00e1c th\u1ef1c)<\/strong><\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>C\u00e1ch 1: In-memory authentication (d\u00f9ng \u0111\u1ec3 demo)<\/strong><\/li>\n<\/ul>\n\n\n\n<p>B\u1ea1n c\u00f3 th\u1ec3 \u0111\u1ecbnh ngh\u0129a user tr\u1ef1c ti\u1ebfp trong code:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ Trong SecurityConfig\n\/\/ import org.springframework.security.core.userdetails.User;\n\/\/ import org.springframework.security.core.userdetails.UserDetails;\n\/\/ import org.springframework.security.core.userdetails.UserDetailsService;\n\/\/ import org.springframework.security.provisioning.InMemoryUserDetailsManager;\n\n@Bean\npublic org.springframework.security.core.userdetails.UserDetailsService inMemoryUserDetailsService(PasswordEncoder passwordEncoder) {\n    org.springframework.security.core.userdetails.UserDetails user = org.springframework.security.core.userdetails.User.builder()\n        .username(\"user\")\n        .password(passwordEncoder.encode(\"pass\"))\n        .roles(\"USER\")\n        .build();\n    org.springframework.security.core.userdetails.UserDetails admin = org.springframework.security.core.userdetails.User.builder()\n        .username(\"admin\")\n        .password(passwordEncoder.encode(\"adminpass\"))\n        .roles(\"ADMIN\", \"USER\")\n        .build();\n    return new org.springframework.security.provisioning.InMemoryUserDetailsManager(user, admin);\n}<\/code><\/pre>\n\n\n\n<p>L\u01b0u \u00fd: S\u1eed d\u1ee5ng <code>BCryptPasswordEncoder<\/code> cho m\u1eadt kh\u1ea9u.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>C\u00e1ch 2: DBC authentication<\/strong><\/li>\n<\/ul>\n\n\n\n<p>L\u1ea5y user t\u1eeb database. C\u1ea7n c\u1ea5u h\u00ecnh <code>DataSource<\/code> v\u00e0 c\u00e1c c\u00e2u l\u1ec7nh SQL ho\u1eb7c schema.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>C\u00e1ch 3: S\u1eed d\u1ee5ng <\/strong><strong>UserDetailsService<\/strong><strong> (khuy\u1ebfn ngh\u1ecb)<\/strong><\/li>\n<\/ul>\n\n\n\n<p>T\u1ea1o service implement <code>UserDetailsService<\/code> \u0111\u1ec3 load user t\u1eeb database<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>package com.example.demo.service;\n\nimport com.example.demo.model.UserAccount; \/\/ Entity ng\u01b0\u1eddi d\u00f9ng\nimport com.example.demo.repository.UserAccountRepository; \/\/ Repository ng\u01b0\u1eddi d\u00f9ng\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\nimport org.springframework.stereotype.Service;\nimport java.util.stream.Collectors;\n\n@Service(\"customUserDetailsService\") \/\/ \u0110\u1eb7t t\u00ean bean \u0111\u1ec3 ph\u00e2n bi\u1ec7t n\u1ebfu c\u00f3 nhi\u1ec1u UserDetailsService\npublic class CustomUserDetailsService implements UserDetailsService {\n    @Autowired private UserAccountRepository userAccountRepository;\n\n    @Override\n    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {\n        UserAccount user = userAccountRepository.findByUsername(username) \/\/ Gi\u1ea3 s\u1eed c\u00f3 ph\u01b0\u01a1ng th\u1ee9c n\u00e0y\n            .orElseThrow(() -&gt; new UsernameNotFoundException(\"User not found: \" + username));\n\n        return new User(user.getUsername(), user.getPassword(),\n            user.getRoles().stream() \/\/ Gi\u1ea3 s\u1eed UserAccount c\u00f3 getRoles() tr\u1ea3 v\u1ec1 Collection&lt;Role&gt;\n                .map(role -&gt; new SimpleGrantedAuthority(role.getName())) \/\/ Gi\u1ea3 s\u1eed Role c\u00f3 getName()\n                .collect(Collectors.toSet()));\n    }\n}<\/code><\/pre>\n\n\n\n<p>Spring Security s\u1ebd t\u1ef1 \u0111\u1ed9ng d\u00f9ng <code>CustomUserDetailsService<\/code> n\u00e0y<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-b\u01b0\u1edbc-3-authorization-phan-quy\u1ec1n\"><strong>B\u01b0\u1edbc 3: Authorization (Ph\u00e2n quy\u1ec1n)<\/strong><\/h3>\n\n\n\n<p>Spring Security gi\u00fap b\u1ea1n c\u00f3 th\u1ec3 ki\u1ec3m so\u00e1t ai truy c\u1eadp c\u00e1i g\u00ec:<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-1-b\u1ea3o-m\u1eadt-c\u1ea5p-url-securityfilterchain\"><strong>1. B\u1ea3o m\u1eadt c\u1ea5p URL (<\/strong><strong>SecurityFilterChain<\/strong><strong>)<\/strong><\/h3>\n\n\n\n<p>\u0110\u1eb7t quy t\u1eafc chung cho c\u00e1c \u0111\u01b0\u1eddng d\u1eabn (URL) c\u1ee7a \u1ee9ng d\u1ee5ng:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong><code>hasRole(\"ADMIN\")<\/code><\/strong>: Ch\u1ec9 user c\u00f3 vai tr\u00f2 <strong>ADMIN<\/strong> \u0111\u01b0\u1ee3c v\u00e0o.<\/li>\n\n\n\n<li><strong><code>hasAuthority(\"READ\")<\/code><\/strong>: Ch\u1ec9 user c\u00f3 quy\u1ec1n <strong>READ<\/strong> \u0111\u01b0\u1ee3c v\u00e0o.<\/li>\n\n\n\n<li><strong><code>permitAll()<\/code><\/strong>: Ai c\u0169ng \u0111\u01b0\u1ee3c v\u00e0o.<\/li>\n\n\n\n<li><strong><code>authenticated()<\/code><\/strong>: Ch\u1ec9 user \u0111\u00e3 \u0111\u0103ng nh\u1eadp \u0111\u01b0\u1ee3c v\u00e0o.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-2-b\u1ea3o-m\u1eadt-c\u1ea5p-ph\u01b0\u01a1ng-th\u1ee9c-enablemethodsecurity\"><strong>2. B\u1ea3o m\u1eadt c\u1ea5p ph\u01b0\u01a1ng th\u1ee9c (<\/strong><strong>@EnableMethodSecurity<\/strong><strong>)<\/strong><\/h3>\n\n\n\n<p>Ki\u1ec3m so\u00e1t chi ti\u1ebft h\u01a1n t\u1eebng h\u00e0m trong code c\u1ee7a b\u1ea1n. Th\u00eam <code>@EnableMethodSecurity(securedEnabled = true)<\/code> v\u00e0o c\u1ea5u h\u00ecnh b\u1ea3o m\u1eadt.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong><code>@Secured({\"ROLE_ADMIN\"})<\/code><\/strong>: Ch\u1ec9 user c\u00f3 <strong>vai tr\u00f2 ADMIN<\/strong> \u0111\u01b0\u1ee3c g\u1ecdi h\u00e0m n\u00e0y.<\/li>\n\n\n\n<li><strong><code>@PreAuthorize(\"hasRole('USER') or hasAuthority('READ')\")<\/code><\/strong>: Ki\u1ec3m tra <strong>tr\u01b0\u1edbc khi h\u00e0m ch\u1ea1y<\/strong>, d\u00f9ng bi\u1ec3u th\u1ee9c ph\u1ee9c t\u1ea1p (SpEL). V\u00ed d\u1ee5: user l\u00e0 USER HO\u1eb6C c\u00f3 quy\u1ec1n READ.<\/li>\n\n\n\n<li><strong><code>@PostAuthorize(\"returnObject.owner == authentication.name\")<\/code><\/strong>: Ki\u1ec3m tra <strong>sau khi h\u00e0m ch\u1ea1y<\/strong>, c\u00f3 th\u1ec3 xem k\u1ebft qu\u1ea3 tr\u1ea3 v\u1ec1. V\u00ed d\u1ee5: ch\u1ec9 ch\u1ee7 s\u1edf h\u1eefu c\u1ee7a d\u1eef li\u1ec7u \u0111\u01b0\u1ee3c ph\u00e9p xem.<\/li>\n<\/ul>\n\n\n\n<p>V\u1ec1 c\u01a1 b\u1ea3n, b\u1ea1n c\u00f3 th\u1ec3 thi\u1ebft l\u1eadp b\u1ea3o m\u1eadt \u1edf c\u1ea5p \u0111\u1ed9 \u0111\u01b0\u1eddng d\u1eabn chung ho\u1eb7c chi ti\u1ebft h\u01a1n \u1edf c\u1ea5p \u0111\u1ed9 t\u1eebng ph\u01b0\u01a1ng th\u1ee9c, t\u00f9y thu\u1ed9c v\u00e0o y\u00eau c\u1ea7u c\u1ee7a \u1ee9ng d\u1ee5ng.<strong>V\u00ed d\u1ee5 trong Service:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ import org.springframework.security.access.prepost.PreAuthorize;\n\/\/ import org.springframework.stereotype.Service;\n\n@org.springframework.stereotype.Service\npublic class SecureService {\n    @org.springframework.security.access.prepost.PreAuthorize(\"hasRole('ADMIN')\")\n    public String getAdminData() { return \"Admin Data\"; }\n\n    @org.springframework.security.access.prepost.PreAuthorize(\"isAuthenticated()\")\n    public String getUserData() { return \"User Data\"; }\n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-hi\u1ec3u-them-v\u1ec1-jwt-json-web-token-nang-cao\"><strong>Hi\u1ec3u th\u00eam v\u1ec1 JWT (JSON Web Token) &#8211; (N\u00e2ng cao)<\/strong><\/h3>\n\n\n\n<p><strong>JWT<\/strong> l\u00e0 m\u1ed9t &#8220;chi\u1ebfc th\u1ebb&#8221; an to\u00e0n ch\u1ee9a th\u00f4ng tin ng\u01b0\u1eddi d\u00f9ng, d\u00f9ng cho x\u00e1c th\u1ef1c m\u00e0 kh\u00f4ng c\u1ea7n l\u01b0u tr\u1ea1ng th\u00e1i tr\u00ean m\u00e1y ch\u1ee7 (stateless).<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p><em>\u0110\u1ecdc th\u00eam: <a href=\"https:\/\/itviec.com\/blog\/json-web-token-la-gi\/\" target=\"_blank\" rel=\"noreferrer noopener\">JSON Web Token l\u00e0 g\u00ec: \u0110\u1ecbnh ngh\u0129a v\u00e0 c\u00e1ch ho\u1ea1t \u0111\u1ed9ng<\/a><\/em><\/p>\n<\/blockquote>\n\n\n\n<p><strong>C\u1ea5u tr\u00fac:<\/strong> <code>Header <\/code>(ki\u1ec3u token\/thu\u1eadt to\u00e1n).<code>Payload<\/code> (th\u00f4ng tin ng\u01b0\u1eddi d\u00f9ng\/claims).<code>Signature<\/code> (ch\u1eef k\u00fd b\u1ea3o m\u1eadt).<\/p>\n\n\n\n<p><strong>C\u00e1ch t\u00edch h\u1ee3p v\u1edbi Spring Security:<\/strong><\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Th\u00eam th\u01b0 vi\u1ec7n JWT<\/strong> \u0111\u1ec3 c\u00f3 th\u1ec3 thao t\u00e1c v\u1edbi token.<\/li>\n\n\n\n<li><strong>API \u0110\u0103ng nh\u1eadp (<\/strong><strong>\/authenticate<\/strong>): Ng\u01b0\u1eddi d\u00f9ng \u0111\u0103ng nh\u1eadp -&gt; n\u1ebfu th\u00e0nh c\u00f4ng, server t\u1ea1o v\u00e0 tr\u1ea3 v\u1ec1 JWT cho client.<\/li>\n\n\n\n<li><strong>L\u1edbp ti\u1ec7n \u00edch JWT<\/strong>: Gi\u00fap b\u1ea1n t\u1ea1o, gi\u1ea3i m\u00e3 v\u00e0 x\u00e1c th\u1ef1c token.<\/li>\n\n\n\n<li><strong>B\u1ed9 l\u1ecdc Request JWT<\/strong>:\n<ul class=\"wp-block-list\">\n<li>V\u1edbi m\u1ed7i y\u00eau c\u1ea7u (tr\u1eeb \u0111\u0103ng nh\u1eadp), b\u1ed9 l\u1ecdc n\u00e0y s\u1ebd ki\u1ec3m tra JWT t\u1eeb client.<\/li>\n\n\n\n<li>N\u1ebfu JWT h\u1ee3p l\u1ec7, ng\u01b0\u1eddi d\u00f9ng \u0111\u01b0\u1ee3c x\u00e1c th\u1ef1c trong Spring Security.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>C\u1ea5u h\u00ecnh Spring Security<\/strong>:\n<ul class=\"wp-block-list\">\n<li><strong>T\u1eaft session<\/strong>: Quan tr\u1ecdng \u0111\u1ec3 JWT ho\u1ea1t \u0111\u1ed9ng stateless.<\/li>\n\n\n\n<li><strong>Th\u00eam b\u1ed9 l\u1ecdc JWT<\/strong>: \u0110\u1ea3m b\u1ea3o token \u0111\u01b0\u1ee3c ki\u1ec3m tra.<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n\n\n\n<p>T\u00f3m l\u1ea1i, JWT gi\u00fap b\u1ea1n x\u00e2y d\u1ef1ng API b\u1ea3o m\u1eadt, n\u01a1i ng\u01b0\u1eddi d\u00f9ng nh\u1eadn token khi \u0111\u0103ng nh\u1eadp v\u00e0 d\u00f9ng token \u0111\u00f3 \u0111\u1ec3 truy c\u1eadp c\u00e1c t\u00e0i nguy\u00ean \u0111\u01b0\u1ee3c b\u1ea3o v\u1ec7 m\u00e0 kh\u00f4ng c\u1ea7n m\u00e1y ch\u1ee7 ph\u1ea3i l\u01b0u tr\u1eef phi\u00ean.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-h\u01b0\u1edbng-d\u1eabn-testing-trong-spring-boot\"><span class=\"ez-toc-section\" id=\"Huong_dan_testing_trong_Spring_Boot\"><\/span><strong>H\u01b0\u1edbng d\u1eabn testing trong Spring Boot<\/strong><span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>Spring Boot \u0111\u01b0\u1ee3c thi\u1ebft k\u1ebf \u0111\u1ec3 h\u1ed7 tr\u1ee3 t\u1ed1i \u0111a vi\u1ec7c testing. N\u00f3 cung c\u1ea5p c\u00e1c c\u00f4ng c\u1ee5 v\u00e0 t\u00edch h\u1ee3p s\u1eb5n gi\u00fap b\u1ea1n vi\u1ebft test d\u1ec5 d\u00e0ng v\u00e0 hi\u1ec7u qu\u1ea3.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-t\u1ea1i-sao-c\u1ea7n-testing\"><strong>T\u1ea1i sao c\u1ea7n Testing?<\/strong><\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>\u0110\u1ea3m b\u1ea3o ch\u1ea5t l\u01b0\u1ee3ng:<\/strong> Testing gi\u00fap b\u1ea1n ph\u00e1t hi\u1ec7n l\u1ed7i s\u1edbm trong qu\u00e1 tr\u00ecnh ph\u00e1t tri\u1ec3n, tr\u01b0\u1edbc khi ch\u00fang g\u00e2y ra v\u1ea5n \u0111\u1ec1 nghi\u00eam tr\u1ecdng cho ng\u01b0\u1eddi d\u00f9ng cu\u1ed1i. Vi\u1ec7c s\u1eeda l\u1ed7i s\u1edbm lu\u00f4n d\u1ec5 d\u00e0ng v\u00e0 \u00edt t\u1ed1n k\u00e9m h\u01a1n nhi\u1ec1u so v\u1edbi vi\u1ec7c s\u1eeda l\u1ed7i khi \u1ee9ng d\u1ee5ng \u0111\u00e3 \u0111\u01b0\u1ee3c tri\u1ec3n khai.<\/li>\n\n\n\n<li><strong>T\u1ef1 tin khi refactor:<\/strong> V\u1edbi c\u00e1c b\u00e0i ki\u1ec3m th\u1eed t\u1ed1t, b\u1ea1n c\u00f3 th\u1ec3 t\u00e1i c\u1ea5u tr\u00fac code m\u1ed9t c\u00e1ch an to\u00e0n v\u00e0 t\u1ef1 tin. C\u00e1c b\u00e0i test c\u1ee7a b\u1ea1n s\u1ebd nh\u01b0 m\u1ed9t &#8220;t\u1ea5m l\u01b0\u1edbi an to\u00e0n&#8221;, l\u1eadp t\u1ee9c b\u00e1o \u0111\u1ed9ng n\u1ebfu b\u1ea5t k\u1ef3 thay \u0111\u1ed5i n\u00e0o ph\u00e1 v\u1ee1 ch\u1ee9c n\u0103ng hi\u1ec7n c\u00f3. \u0110i\u1ec1u n\u00e0y khuy\u1ebfn kh\u00edch vi\u1ec7c c\u1ea3i thi\u1ec7n ch\u1ea5t l\u01b0\u1ee3ng code li\u00ean t\u1ee5c.<\/li>\n\n\n\n<li><strong>T\u00e0i li\u1ec7u s\u1ed1ng:<\/strong> M\u1ed7i test case kh\u00f4ng ch\u1ec9 ki\u1ec3m tra m\u1ed9t ch\u1ee9c n\u0103ng m\u00e0 c\u00f2n m\u00f4 t\u1ea3 c\u00e1ch ch\u1ee9c n\u0103ng \u0111\u00f3 ho\u1ea1t \u0111\u1ed9ng trong c\u00e1c tr\u01b0\u1eddng h\u1ee3p kh\u00e1c nhau. Khi dev m\u1edbi tham gia d\u1ef1 \u00e1n, h\u1ecd c\u00f3 th\u1ec3 \u0111\u1ecdc c\u00e1c test case \u0111\u1ec3 nhanh ch\u00f3ng hi\u1ec3u \u0111\u01b0\u1ee3c m\u1ee5c \u0111\u00edch v\u00e0 h\u00e0nh vi c\u1ee7a c\u00e1c ph\u1ea7n m\u1ec1m m\u00e0 kh\u00f4ng c\u1ea7n \u0111\u1ecdc qua h\u00e0ng ng\u00e0n d\u00f2ng code logic nghi\u1ec7p v\u1ee5.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-cac-lo\u1ea1i-test\"><strong>C\u00e1c lo\u1ea1i Test<\/strong><\/h3>\n\n\n\n<p><strong>Unit Tests (Ki\u1ec3m th\u1eed \u0111\u01a1n v\u1ecb):<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>M\u1ee5c ti\u00eau:<\/strong> Ki\u1ec3m tra m\u1ed9t \u0111\u01a1n v\u1ecb code nh\u1ecf (ph\u01b0\u01a1ng th\u1ee9c, class) c\u00f4 l\u1eadp.<\/li>\n\n\n\n<li><strong>\u0110\u1eb7c \u0111i\u1ec3m:<\/strong> Nhanh, kh\u00f4ng ph\u1ee5 thu\u1ed9c Spring context\/DB. Dependencies \u0111\u01b0\u1ee3c &#8220;mock&#8221;.<\/li>\n\n\n\n<li><strong>V\u00ed d\u1ee5:<\/strong> B\u1ea1n s\u1ebd d\u00f9ng c\u00e1c &#8220;c\u00f4ng c\u1ee5&#8221; nh\u01b0 JUnit v\u00e0 Mockito. L\u00fac n\u00e0y, b\u1ea1n ch\u1ec9 t\u1eadp trung v\u00e0o logic c\u1ee7a h\u00e0m \u0111\u00f3 th\u00f4i, kh\u00f4ng c\u1ea7n &#8220;\u0111\u1ee5ng ch\u1ea1m&#8221; g\u00ec \u0111\u1ebfn database hay c\u00e1c d\u1ecbch v\u1ee5 kh\u00e1c \u0111\u00e2u.<\/li>\n<\/ul>\n\n\n\n<p><strong>Integration Tests (Ki\u1ec3m th\u1eed t\u00edch h\u1ee3p):<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>M\u1ee5c ti\u00eau:<\/strong> Ki\u1ec3m tra t\u01b0\u01a1ng t\u00e1c gi\u1eefa c\u00e1c th\u00e0nh ph\u1ea7n ho\u1eb7c v\u1edbi h\u1ec7 th\u1ed1ng ngo\u00e0i.<\/li>\n\n\n\n<li><strong>\u0110\u1eb7c \u0111i\u1ec3m:<\/strong> Ch\u1eadm h\u01a1n, c\u00f3 th\u1ec3 c\u1ea7n Spring context, DB (th\u01b0\u1eddng l\u00e0 in-memory).<\/li>\n\n\n\n<li><strong>V\u00ed d\u1ee5:<\/strong> B\u1ea1n s\u1ebd d\u00f9ng @SpringBootTest \u0111\u1ec3 &#8220;kh\u1edfi \u0111\u1ed9ng&#8221; m\u1ed9t ph\u1ea7n (ho\u1eb7c to\u00e0n b\u1ed9) \u1ee9ng d\u1ee5ng Spring l\u00ean, v\u00e0 th\u01b0\u1eddng d\u00f9ng c\u00e1c database &#8220;mini&#8221; (nh\u01b0 H2) ho\u1eb7c Testcontainers \u0111\u1ec3 ki\u1ec3m tra t\u01b0\u01a1ng t\u00e1c v\u1edbi database y nh\u01b0 th\u1eadt.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-cong-c\u1ee5-va-dependencies\"><strong>C\u00f4ng c\u1ee5 v\u00e0 Dependencies<\/strong><\/h3>\n\n\n\n<p>spring-boot-starter-test bao g\u1ed3m:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>JUnit 5:<\/strong> Framework ki\u1ec3m th\u1eed.<\/li>\n\n\n\n<li><strong>Spring Test &amp; Spring Boot Test:<\/strong> H\u1ed7 tr\u1ee3 test \u1ee9ng d\u1ee5ng Spring.<\/li>\n\n\n\n<li><strong>Mockito:<\/strong> T\u1ea1o \u0111\u1ed1i t\u01b0\u1ee3ng gi\u1ea3 (mock).<\/li>\n\n\n\n<li><strong>AssertJ:<\/strong> Th\u01b0 vi\u1ec7n assertion.<\/li>\n<\/ul>\n\n\n\n<p><strong>Dependency (<\/strong><strong>pom.xml<\/strong><strong>):<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;dependency&gt;\n    &lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt;\n    &lt;artifactId&gt;spring-boot-starter-test&lt;\/artifactId&gt;\n    &lt;scope&gt;test&lt;\/scope&gt;\n&lt;\/dependency&gt;<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-cach-vi\u1ebft-unit-test-cho-services\"><strong>C\u00e1ch vi\u1ebft Unit Test cho Services<\/strong><\/h3>\n\n\n\n<p><strong>D\u00f9ng Mockito \u0111\u1ec3 mock dependencies:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>@Mock<\/code>: T\u1ea1o mock.<\/li>\n\n\n\n<li><code>@InjectMocks<\/code>: Inject mock v\u00e0o class \u0111ang test.<\/li>\n\n\n\n<li><code>when(...).thenReturn(...)<\/code>: \u0110\u1ecbnh ngh\u0129a h\u00e0nh vi mock.<\/li>\n\n\n\n<li><code>@ExtendWith(MockitoExtension.class)<\/code>: K\u00edch ho\u1ea1t Mockito.<\/li>\n<\/ul>\n\n\n\n<p><strong>V\u00ed d\u1ee5 Unit Test <code>ProductService<\/code>:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>package com.example.demo.service;\n\nimport com.example.demo.model.Product;\nimport com.example.demo.repository.ProductRepository;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport java.util.Optional;\nimport static org.junit.jupiter.api.Assertions.*;\nimport static org.mockito.Mockito.*;\n\n@ExtendWith(MockitoExtension.class)\nclass ProductServiceTest {\n    @Mock private ProductRepository repoMock;\n    @InjectMocks private ProductService service;\n\n    @Test\n    void testGetProductById_Exists() {\n        Product p = new Product(\"Test\", 10.0, 1); p.setId(1L);\n        when(repoMock.findById(1L)).thenReturn(Optional.of(p));\n        Product found = service.getProductById(1L);\n        assertNotNull(found);\n        assertEquals(\"Test\", found.getName());\n        verify(repoMock).findById(1L);\n    }\n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-cach-vi\u1ebft-integration-test-cho-controllers-va-repositories\"><strong>C\u00e1ch vi\u1ebft Integration Test cho Controllers v\u00e0 Repositories<\/strong><\/h3>\n\n\n\n<p><strong>Annotations h\u1ed7 tr\u1ee3:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>@SpringBootTest<\/code>: Load to\u00e0n b\u1ed9 Spring context.<\/li>\n\n\n\n<li><code>@AutoConfigureMockMvc<\/code>: C\u1ea5u h\u00ecnh MockMvc (\u0111i k\u00e8m @SpringBootTest).<\/li>\n\n\n\n<li><code>@DataJpaTest<\/code>: Ch\u1ec9 load JPA components, d\u00f9ng H2, rollback sau test.<\/li>\n\n\n\n<li><code>@WebMvcTest(YourController.class)<\/code>: Ch\u1ec9 load context cho m\u1ed9t Controller (slice test).<\/li>\n<\/ul>\n\n\n\n<p><strong>D\u00f9ng <\/strong><strong>MockMvc<\/strong><strong> \u0111\u1ec3 test API endpoints:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>mockMvc.perform(get(\"\/api\/path\")...)<\/code>: Th\u1ef1c hi\u1ec7n request.<\/li>\n\n\n\n<li><code>.andExpect(status().isOk())<\/code>: Ki\u1ec3m tra status.<\/li>\n\n\n\n<li><code>.andExpect(jsonPath(\"$.field\").value(...))<\/code>: Ki\u1ec3m tra JSON response.<\/li>\n<\/ul>\n\n\n\n<p><strong>V\u00ed d\u1ee5 Integration <code>Test ProductController<\/code> (<code>@WebMvcTest<\/code>):<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>package com.example.demo.controller;\n\nimport com.example.demo.model.Product;\nimport com.example.demo.service.ProductService;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;\nimport org.springframework.boot.test.mock.mockito.MockBean;\nimport org.springframework.http.MediaType;\nimport org.springframework.test.web.servlet.MockMvc;\nimport static org.mockito.BDDMockito.given;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;\nimport static org.hamcrest.CoreMatchers.is;\n\n@WebMvcTest(ProductController.class)\nclass ProductControllerTest {\n    @Autowired private MockMvc mockMvc;\n    @MockBean private ProductService serviceMock; \/\/ Mock service\n    @Autowired private ObjectMapper objectMapper;\n\n    @Test\n    void testGetProductById_Exists() throws Exception {\n        Product p = new Product(\"API Test\", 100.0, 5); p.setId(1L);\n        given(serviceMock.getProductById(1L)).willReturn(p);\n\n        mockMvc.perform(get(\"\/api\/v1\/products\/{id}\", 1L))\n            .andExpect(status().isOk())\n            .andExpect(jsonPath(\"$.name\", is(\"API Test\")));\n    }\n}<\/code><\/pre>\n\n\n\n<p><strong>V\u00ed d\u1ee5 Integration Test <code>ProductRepository<\/code> (<code>@DataJpaTest<\/code>):<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>package com.example.demo.repository;\n\nimport com.example.demo.model.Product;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;\nimport org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;\nimport java.util.Optional;\nimport static org.assertj.core.api.Assertions.assertThat;\n\n@DataJpaTest\nclass ProductRepositoryTest {\n    @Autowired private TestEntityManager entityManager;\n    @Autowired private ProductRepository repository;\n\n    @Test\n    void testSaveAndFind() {\n        Product p = entityManager.persistFlushFind(new Product(\"Repo Test\", 20.0, 2));\n        Optional&lt;Product&gt; found = repository.findById(p.getId());\n        assertThat(found).isPresent();\n        assertThat(found.get().getName()).isEqualTo(\"Repo Test\");\n    }\n}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-cac-cau-h\u1ecfi-th\u01b0\u1eddng-g\u1eb7p-v\u1ec1-spring-boot\"><span class=\"ez-toc-section\" id=\"Cac_cau_hoi_thuong_gap_ve_Spring_Boot\"><\/span><strong>C\u00e1c c\u00e2u h\u1ecfi th\u01b0\u1eddng g\u1eb7p v\u1ec1 Spring Boot<\/strong><span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-lam-th\u1ebf-nao-d\u1ec3-tri\u1ec3n-khai-xac-th\u1ef1c-b\u1eb1ng-jwt-json-web-token-thay-vi-form-login\"><strong>L\u00e0m th\u1ebf n\u00e0o \u0111\u1ec3 tri\u1ec3n khai x\u00e1c th\u1ef1c b\u1eb1ng JWT (JSON Web Token) thay v\u00ec Form Login?<\/strong><\/h3>\n\n\n\n<p>\u0110\u00e2y l\u00e0 m\u1ed9t ch\u1ee7 \u0111\u1ec1 n\u00e2ng cao v\u00e0 l\u00e0 b\u01b0\u1edbc \u0111i ph\u1ed5 bi\u1ebfn \u0111\u1ec3 x\u00e2y d\u1ef1ng c\u00e1c API stateless. C\u00e1c b\u01b0\u1edbc ch\u00ednh bao g\u1ed3m:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Th\u00eam dependency JWT<\/strong> (v\u00ed d\u1ee5: <code>io.jsonwebtoken<\/code>).<\/li>\n\n\n\n<li><strong>T\u1ea1o m\u1ed9t endpoint \u0111\u0103ng nh\u1eadp<\/strong> (<code>\/api\/auth\/login<\/code>) kh\u00f4ng \u0111\u01b0\u1ee3c b\u1ea3o v\u1ec7. Endpoint n\u00e0y s\u1ebd x\u00e1c th\u1ef1c username\/password, n\u1ebfu th\u00e0nh c\u00f4ng th\u00ec t\u1ea1o v\u00e0 tr\u1ea3 v\u1ec1 m\u1ed9t JWT.<\/li>\n\n\n\n<li><strong>Client l\u01b0u JWT<\/strong> v\u00e0 g\u1eedi k\u00e8m trong header <code>Authorization: Bearer &lt;token&gt;<\/code> cho m\u1ed7i request ti\u1ebfp theo.<\/li>\n\n\n\n<li><strong>T\u1ea1o m\u1ed9t JWT Filter<\/strong> t\u00f9y ch\u1ec9nh trong Spring Security. Filter n\u00e0y s\u1ebd ki\u1ec3m tra v\u00e0 x\u00e1c th\u1ef1c token t\u1eeb m\u1ed7i request. N\u1ebfu token h\u1ee3p l\u1ec7, n\u00f3 s\u1ebd thi\u1ebft l\u1eadp th\u00f4ng tin x\u00e1c th\u1ef1c cho request \u0111\u00f3.<\/li>\n\n\n\n<li><strong>C\u1ea5u h\u00ecnh Spring Security<\/strong> \u0111\u1ec3 s\u1eed d\u1ee5ng filter n\u00e0y v\u00e0 ho\u1ea1t \u0111\u1ed9ng \u1edf ch\u1ebf \u0111\u1ed9 stateless.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-t\u1ea1i-sao-nen-dung-dto-data-transfer-object-thay-vi-dung-tr\u1ef1c-ti\u1ebfp-entity\"><strong>T\u1ea1i sao n\u00ean d\u00f9ng DTO (Data Transfer Object) thay v\u00ec d\u00f9ng tr\u1ef1c ti\u1ebfp Entity?<\/strong><\/h3>\n\n\n\n<p>\u0110\u00e2y l\u00e0 m\u1ed9t th\u1ef1c h\u00e0nh t\u1ed1t (good practice) v\u00ec nhi\u1ec1u l\u00fd do quan tr\u1ecdng:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>B\u1ea3o m\u1eadt:<\/strong> B\u1ea1n c\u00f3 th\u1ec3 che gi\u1ea5u c\u00e1c tr\u01b0\u1eddng nh\u1ea1y c\u1ea3m c\u1ee7a Entity (v\u00ed d\u1ee5: password) kh\u00f4ng cho hi\u1ec3n th\u1ecb ra API.<\/li>\n\n\n\n<li><strong>T\u00ednh \u1ed5n \u0111\u1ecbnh c\u1ee7a API:<\/strong> C\u1ea5u tr\u00fac database (Entity) c\u00f3 th\u1ec3 thay \u0111\u1ed5i, nh\u01b0ng b\u1ea1n c\u00f3 th\u1ec3 gi\u1eef nguy\u00ean c\u1ea5u tr\u00fac c\u1ee7a DTO \u0111\u1ec3 API kh\u00f4ng b\u1ecb thay \u0111\u1ed5i, tr\u00e1nh l\u00e0m h\u1ecfng c\u00e1c \u1ee9ng d\u1ee5ng client \u0111ang s\u1eed d\u1ee5ng.<\/li>\n\n\n\n<li><strong>Linh ho\u1ea1t:<\/strong> DTO cho ph\u00e9p b\u1ea1n \u0111\u1ecbnh h\u00ecnh d\u1eef li\u1ec7u tr\u1ea3 v\u1ec1 theo \u0111\u00fang nhu c\u1ea7u c\u1ee7a client, c\u00f3 th\u1ec3 g\u1ed9p ho\u1eb7c t\u00ednh to\u00e1n th\u00eam c\u00e1c tr\u01b0\u1eddng m\u1edbi m\u00e0 kh\u00f4ng c\u00f3 trong Entity.<\/li>\n\n\n\n<li><strong>Tr\u00e1nh l\u1ed7i Lazy Loading:<\/strong> Khi l\u00e0m vi\u1ec7c v\u1edbi JPA, vi\u1ec7c tr\u1ea3 v\u1ec1 tr\u1ef1c ti\u1ebfp Entity c\u00f3 th\u1ec3 g\u00e2y ra l\u1ed7i <code>LazyInitializationException<\/code> n\u1ebfu b\u1ea1n c\u1ed1 g\u1eafng truy c\u1eadp c\u00e1c m\u1ed1i quan h\u1ec7 \u0111\u01b0\u1ee3c t\u1ea3i l\u01b0\u1eddi (lazy-loaded).<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-lam-th\u1ebf-nao-spring-boot-t\u1ef1-d\u1ed9ng-chuy\u1ec3n-d\u1ed5i-d\u1ed1i-t\u01b0\u1ee3ng-java-c\u1ee7a-toi-thanh-json\"><strong>L\u00e0m th\u1ebf n\u00e0o Spring Boot t\u1ef1 \u0111\u1ed9ng chuy\u1ec3n \u0111\u1ed5i \u0111\u1ed1i t\u01b0\u1ee3ng Java c\u1ee7a t\u00f4i th\u00e0nh JSON?<\/strong><\/h3>\n\n\n\n<p>Khi b\u1ea1n s\u1eed d\u1ee5ng <code>@RestController<\/code> v\u00e0 tr\u1ea3 v\u1ec1 m\u1ed9t \u0111\u1ed1i t\u01b0\u1ee3ng Java, &#8220;ph\u00e9p thu\u1eadt&#8221; n\u00e0y x\u1ea3y ra l\u00e0 nh\u1edd:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Dependency spring-boot-starter-web \u0111\u00e3 t\u00edch h\u1ee3p s\u1eb5n th\u01b0 vi\u1ec7n <strong>Jackson<\/strong>.<\/li>\n\n\n\n<li>Jackson l\u00e0 m\u1ed9t th\u01b0 vi\u1ec7n m\u1ea1nh m\u1ebd chuy\u00ean x\u1eed l\u00fd JSON trong Java. N\u00f3 s\u1ebd t\u1ef1 \u0111\u1ed9ng &#8220;tu\u1ea7n t\u1ef1 h\u00f3a&#8221; (serialize) \u0111\u1ed1i t\u01b0\u1ee3ng Java c\u1ee7a b\u1ea1n th\u00e0nh m\u1ed9t chu\u1ed7i JSON v\u00e0 ghi v\u00e0o HTTP response body.<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-spring-boot-va-spring-framework-khac-nhau-nh\u01b0-th\u1ebf-nao\"><strong>Spring Boot v\u00e0 Spring Framework kh\u00e1c nhau nh\u01b0 th\u1ebf n\u00e0o?<\/strong><\/h3>\n\n\n\n<p>\u0110\u00e2y l\u00e0 m\u1ed9t c\u00e2u h\u1ecfi r\u1ea5t ph\u1ed5 bi\u1ebfn. H\u00e3y ngh\u0129 \u0111\u01a1n gi\u1ea3n nh\u01b0 sau:<\/p>\n\n\n\n<p><strong><a href=\"https:\/\/itviec.com\/blog\/spring-framework\/\" target=\"_blank\" rel=\"noreferrer noopener\">Spring Framework<\/a><\/strong> l\u00e0 m\u1ed9t b\u1ed9 khung (framework) l\u1edbn v\u00e0 m\u1ea1nh m\u1ebd, cung c\u1ea5p c\u00e1c module to\u00e0n di\u1ec7n (nh\u01b0 IoC\/DI, AOP, Spring MVC) \u0111\u1ec3 x\u00e2y d\u1ef1ng \u1ee9ng d\u1ee5ng Java. Tuy nhi\u00ean, vi\u1ec7c c\u1ea5u h\u00ecnh m\u1ed9t d\u1ef1 \u00e1n Spring t\u1eeb \u0111\u1ea7u c\u00f3 th\u1ec3 kh\u00e1 ph\u1ee9c t\u1ea1p v\u00e0 t\u1ed1n th\u1eddi gian.<\/p>\n\n\n\n<p><strong>Spring Boot<\/strong> l\u00e0 m\u1ed9t ph\u1ea7n m\u1edf r\u1ed9ng c\u1ee7a Spring Framework, \u0111\u01b0\u1ee3c t\u1ea1o ra \u0111\u1ec3 <strong>\u0111\u01a1n gi\u1ea3n h\u00f3a<\/strong> qu\u00e1 tr\u00ecnh n\u00e0y. N\u00f3 kh\u00f4ng thay th\u1ebf Spring Framework m\u00e0 gi\u00fap b\u1ea1n &#8220;kh\u1edfi \u0111\u1ed9ng&#8221; \u1ee9ng d\u1ee5ng Spring m\u1ed9t c\u00e1ch nhanh ch\u00f3ng v\u1edbi c\u00e1c tri\u1ebft l\u00fd:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Auto-configuration (T\u1ef1 \u0111\u1ed9ng c\u1ea5u h\u00ecnh):<\/strong> T\u1ef1 \u0111\u1ed9ng c\u1ea5u h\u00ecnh \u1ee9ng d\u1ee5ng d\u1ef1a tr\u00ean c\u00e1c dependency b\u1ea1n th\u00eam v\u00e0o.<\/li>\n\n\n\n<li><strong>Opinionated defaults (C\u1ea5u h\u00ecnh m\u1eb7c \u0111\u1ecbnh c\u00f3 ch\u1ee7 \u0111\u00edch):<\/strong> Cung c\u1ea5p c\u00e1c c\u1ea5u h\u00ecnh m\u1eb7c \u0111\u1ecbnh t\u1ed1i \u01b0u, gi\u00fap b\u1ea1n ch\u1ea1y ngay m\u00e0 kh\u00f4ng c\u1ea7n tinh ch\u1ec9nh nhi\u1ec1u.<\/li>\n\n\n\n<li><strong>Embedded server (Server nh\u00fang):<\/strong> T\u00edch h\u1ee3p s\u1eb5n server (nh\u01b0 Tomcat) \u0111\u1ec3 b\u1ea1n c\u00f3 th\u1ec3 ch\u1ea1y \u1ee9ng d\u1ee5ng nh\u01b0 m\u1ed9t file JAR \u0111\u1ed9c l\u1eadp.<\/li>\n<\/ul>\n\n\n\n<p>T\u00f3m l\u1ea1i: Spring Boot gi\u00fap b\u1ea1n s\u1eed d\u1ee5ng Spring Framework m\u1ed9t c\u00e1ch d\u1ec5 d\u00e0ng h\u01a1n r\u1ea5t nhi\u1ec1u.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-t\u1ed5ng-k\u1ebft\"><span class=\"ez-toc-section\" id=\"Tong_ket\"><\/span><strong>T\u1ed5ng k\u1ebft<\/strong><span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>Qua h\u01b0\u1edbng d\u1eabn n\u00e0y, b\u1ea1n \u0111\u00e3 n\u1eafm \u0111\u01b0\u1ee3c c\u00e1c k\u1ef9 n\u0103ng c\u1ed1t l\u00f5i \u0111\u1ec3 x\u00e2y d\u1ef1ng m\u1ed9t \u1ee9ng d\u1ee5ng Spring Boot th\u1ef1c t\u1ebf. B\u1ea1n \u0111\u00e3 h\u1ecdc c\u00e1ch t\u1ea1o API, l\u00e0m vi\u1ec7c v\u1edbi database, b\u1ea3o m\u1eadt v\u00e0 ki\u1ec3m th\u1eed \u1ee9ng d\u1ee5ng. S\u1ee9c m\u1ea1nh c\u1ee7a Spring Boot n\u1eb1m \u1edf kh\u1ea3 n\u0103ng gi\u00fap nh\u00e0 ph\u00e1t tri\u1ec3n gi\u1ea3m thi\u1ec3u c\u1ea5u h\u00ecnh ph\u1ee9c t\u1ea1p, t\u1eadp trung v\u00e0o vi\u1ec7c vi\u1ebft logic nghi\u1ec7p v\u1ee5, t\u1eeb \u0111\u00f3 t\u0103ng t\u1ed1c \u0111\u1ed9 ph\u00e1t tri\u1ec3n v\u00e0 n\u00e2ng cao hi\u1ec7u su\u1ea5t c\u00f4ng vi\u1ec7c.<\/p>\n\n\n\n<p>Vi\u1ec7c t\u1ef1 \u0111\u1ed9ng c\u1ea5u h\u00ecnh (auto-configuration), c\u00e1c &#8220;starter&#8221; ti\u1ec7n l\u1ee3i v\u00e0 server t\u00edch h\u1ee3p s\u1eb5n ch\u1ec9 l\u00e0 m\u1ed9t v\u00e0i trong s\u1ed1 r\u1ea5t nhi\u1ec1u t\u00ednh n\u0103ng gi\u00fap Spring Boot tr\u1edf th\u00e0nh l\u1ef1a ch\u1ecdn h\u00e0ng \u0111\u1ea7u cho vi\u1ec7c ph\u00e1t tri\u1ec3n c\u00e1c \u1ee9ng d\u1ee5ng microservices v\u00e0 \u1ee9ng d\u1ee5ng web hi\u1ec7n \u0111\u1ea1i.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Spring Framework n\u1ed5i ti\u1ebfng nh\u1edd t\u00ednh m\u1ea1nh m\u1ebd, linh ho\u1ea1t, nh\u01b0ng c\u0169ng n\u1ed5i ti\u1ebfng v\u1edbi s\u1ef1 ph\u1ee9c t\u1ea1p. \u0110\u1ec3 gi\u1ea3i quy\u1ebft b\u00e0i to\u00e1n n\u00e0y, Spring Boot \u0111\u00e3 ra \u0111\u1eddi nh\u01b0 m\u1ed9t ph\u1ea7n m\u1edf r\u1ed9ng c\u1ee7a Spring Framework, \u0111\u01b0\u1ee3c t\u1ea1o ra \u0111\u1ec3 \u0111\u01a1n gi\u1ea3n h\u00f3a qu\u00e1 tr\u00ecnh thi\u1ebft l\u1eadp v\u00e0 ph\u00e1t tri\u1ec3n \u1ee9ng d\u1ee5ng. B\u00e0i h\u01b0\u1edbng [&hellip;]<\/p>\n","protected":false},"author":203,"featured_media":89029,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_gspb_post_css":"","footnotes":""},"categories":[109],"tags":[],"class_list":["post-88809","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-chuyen-mon-it"],"blocksy_meta":[],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v26.8 (Yoast SEO v27.9) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>Spring Boot tutorial chi ti\u1ebft t\u1eeb A-Z cho l\u1eadp tr\u00ecnh vi\u00ean Java - ITviec Blog<\/title>\n<meta name=\"description\" content=\"Th\u1ef1c h\u00e0nh Spring Boot tutorial chi ti\u1ebft t\u1eeb c\u00e0i \u0111\u1eb7t IDE, t\u1ea1o d\u1ef1 \u00e1n, build RESTful API, k\u1ebft n\u1ed1i DB, b\u1ea3o m\u1eadt v\u00e0 testing\u2026 v\u1edbi v\u00ed d\u1ee5 d\u1ec5 hi\u1ec3u.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/itviec.com\/blog\/huong-dan-spring-boot-tutorial\/\" \/>\n<meta property=\"og:locale\" content=\"vi_VN\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Spring Boot tutorial chi ti\u1ebft t\u1eeb A-Z cho l\u1eadp tr\u00ecnh vi\u00ean Java\" \/>\n<meta property=\"og:description\" content=\"Spring Framework n\u1ed5i ti\u1ebfng nh\u1edd t\u00ednh m\u1ea1nh m\u1ebd, linh ho\u1ea1t, nh\u01b0ng c\u0169ng n\u1ed5i ti\u1ebfng v\u1edbi s\u1ef1 ph\u1ee9c t\u1ea1p. \u0110\u1ec3 gi\u1ea3i quy\u1ebft b\u00e0i to\u00e1n n\u00e0y, Spring Boot \u0111\u00e3 ra \u0111\u1eddi nh\u01b0 m\u1ed9t\" \/>\n<meta property=\"og:url\" content=\"https:\/\/itviec.com\/blog\/huong-dan-spring-boot-tutorial\/\" \/>\n<meta property=\"og:site_name\" content=\"ITviec Blog\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/ITviec\" \/>\n<meta property=\"article:published_time\" content=\"2025-07-06T14:49:30+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-07-06T14:49:34+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/itviec.com\/blog\/wp-content\/uploads\/2025\/07\/spring-boot-tutorial-scaled.png\" \/>\n\t<meta property=\"og:image:width\" content=\"2560\" \/>\n\t<meta property=\"og:image:height\" content=\"1347\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Tien Tran\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@ITviec\" \/>\n<meta name=\"twitter:site\" content=\"@ITviec\" \/>\n<meta name=\"twitter:label1\" content=\"\u0110\u01b0\u1ee3c vi\u1ebft b\u1edfi\" \/>\n\t<meta name=\"twitter:data1\" content=\"Tien Tran\" \/>\n\t<meta name=\"twitter:label2\" content=\"\u01af\u1edbc t\u00ednh th\u1eddi gian \u0111\u1ecdc\" \/>\n\t<meta name=\"twitter:data2\" content=\"33 ph\u00fat\" \/>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Spring Boot tutorial chi ti\u1ebft t\u1eeb A-Z cho l\u1eadp tr\u00ecnh vi\u00ean Java - ITviec Blog","description":"Th\u1ef1c h\u00e0nh Spring Boot tutorial chi ti\u1ebft t\u1eeb c\u00e0i \u0111\u1eb7t IDE, t\u1ea1o d\u1ef1 \u00e1n, build RESTful API, k\u1ebft n\u1ed1i DB, b\u1ea3o m\u1eadt v\u00e0 testing\u2026 v\u1edbi v\u00ed d\u1ee5 d\u1ec5 hi\u1ec3u.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/itviec.com\/blog\/huong-dan-spring-boot-tutorial\/","og_locale":"vi_VN","og_type":"article","og_title":"Spring Boot tutorial chi ti\u1ebft t\u1eeb A-Z cho l\u1eadp tr\u00ecnh vi\u00ean Java","og_description":"Spring Framework n\u1ed5i ti\u1ebfng nh\u1edd t\u00ednh m\u1ea1nh m\u1ebd, linh ho\u1ea1t, nh\u01b0ng c\u0169ng n\u1ed5i ti\u1ebfng v\u1edbi s\u1ef1 ph\u1ee9c t\u1ea1p. \u0110\u1ec3 gi\u1ea3i quy\u1ebft b\u00e0i to\u00e1n n\u00e0y, Spring Boot \u0111\u00e3 ra \u0111\u1eddi nh\u01b0 m\u1ed9t","og_url":"https:\/\/itviec.com\/blog\/huong-dan-spring-boot-tutorial\/","og_site_name":"ITviec Blog","article_publisher":"https:\/\/www.facebook.com\/ITviec","article_published_time":"2025-07-06T14:49:30+00:00","article_modified_time":"2025-07-06T14:49:34+00:00","og_image":[{"width":2560,"height":1347,"url":"https:\/\/itviec.com\/blog\/wp-content\/uploads\/2025\/07\/spring-boot-tutorial-scaled.png","type":"image\/png"}],"author":"Tien Tran","twitter_card":"summary_large_image","twitter_creator":"@ITviec","twitter_site":"@ITviec","twitter_misc":{"\u0110\u01b0\u1ee3c vi\u1ebft b\u1edfi":"Tien Tran","\u01af\u1edbc t\u00ednh th\u1eddi gian \u0111\u1ecdc":"33 ph\u00fat"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/itviec.com\/blog\/huong-dan-spring-boot-tutorial\/#article","isPartOf":{"@id":"https:\/\/itviec.com\/blog\/huong-dan-spring-boot-tutorial\/"},"author":{"name":"Tien Tran","@id":"https:\/\/itviec.com\/blog\/#\/schema\/person\/1595d671c49cfa2a48cd3c0a047a1298"},"headline":"Spring Boot tutorial chi ti\u1ebft t\u1eeb A-Z cho l\u1eadp tr\u00ecnh vi\u00ean Java","datePublished":"2025-07-06T14:49:30+00:00","dateModified":"2025-07-06T14:49:34+00:00","mainEntityOfPage":{"@id":"https:\/\/itviec.com\/blog\/huong-dan-spring-boot-tutorial\/"},"wordCount":8811,"publisher":{"@id":"https:\/\/itviec.com\/blog\/#organization"},"image":{"@id":"https:\/\/itviec.com\/blog\/huong-dan-spring-boot-tutorial\/#primaryimage"},"thumbnailUrl":"https:\/\/itviec.com\/blog\/wp-content\/uploads\/2025\/07\/spring-boot-tutorial-scaled.png","articleSection":["Chuy\u00ean m\u00f4n IT"],"inLanguage":"vi"},{"@type":"WebPage","@id":"https:\/\/itviec.com\/blog\/huong-dan-spring-boot-tutorial\/","url":"https:\/\/itviec.com\/blog\/huong-dan-spring-boot-tutorial\/","name":"Spring Boot tutorial chi ti\u1ebft t\u1eeb A-Z cho l\u1eadp tr\u00ecnh vi\u00ean Java - ITviec Blog","isPartOf":{"@id":"https:\/\/itviec.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/itviec.com\/blog\/huong-dan-spring-boot-tutorial\/#primaryimage"},"image":{"@id":"https:\/\/itviec.com\/blog\/huong-dan-spring-boot-tutorial\/#primaryimage"},"thumbnailUrl":"https:\/\/itviec.com\/blog\/wp-content\/uploads\/2025\/07\/spring-boot-tutorial-scaled.png","datePublished":"2025-07-06T14:49:30+00:00","dateModified":"2025-07-06T14:49:34+00:00","description":"Th\u1ef1c h\u00e0nh Spring Boot tutorial chi ti\u1ebft t\u1eeb c\u00e0i \u0111\u1eb7t IDE, t\u1ea1o d\u1ef1 \u00e1n, build RESTful API, k\u1ebft n\u1ed1i DB, b\u1ea3o m\u1eadt v\u00e0 testing\u2026 v\u1edbi v\u00ed d\u1ee5 d\u1ec5 hi\u1ec3u.","breadcrumb":{"@id":"https:\/\/itviec.com\/blog\/huong-dan-spring-boot-tutorial\/#breadcrumb"},"inLanguage":"vi","potentialAction":[{"@type":"ReadAction","target":["https:\/\/itviec.com\/blog\/huong-dan-spring-boot-tutorial\/"]}]},{"@type":"ImageObject","inLanguage":"vi","@id":"https:\/\/itviec.com\/blog\/huong-dan-spring-boot-tutorial\/#primaryimage","url":"https:\/\/itviec.com\/blog\/wp-content\/uploads\/2025\/07\/spring-boot-tutorial-scaled.png","contentUrl":"https:\/\/itviec.com\/blog\/wp-content\/uploads\/2025\/07\/spring-boot-tutorial-scaled.png","width":800,"height":421,"caption":"spring boot tutorial - itviec blog"},{"@type":"BreadcrumbList","@id":"https:\/\/itviec.com\/blog\/huong-dan-spring-boot-tutorial\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Chuy\u00ean m\u00f4n IT","item":"https:\/\/itviec.com\/blog\/chuyen-mon-it\/"},{"@type":"ListItem","position":2,"name":"Spring Boot tutorial chi ti\u1ebft t\u1eeb A-Z cho l\u1eadp tr\u00ecnh vi\u00ean Java"}]},{"@type":"WebSite","@id":"https:\/\/itviec.com\/blog\/#website","url":"https:\/\/itviec.com\/blog\/","name":"ITviec Blog","description":"IT Jobs &amp; People in Vietnam","publisher":{"@id":"https:\/\/itviec.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/itviec.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"vi"},{"@type":"Organization","@id":"https:\/\/itviec.com\/blog\/#organization","name":"ITviec","url":"https:\/\/itviec.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"vi","@id":"https:\/\/itviec.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/itviec.com\/blog\/wp-content\/uploads\/2018\/12\/itviec-black-square-facebook.png","contentUrl":"https:\/\/itviec.com\/blog\/wp-content\/uploads\/2018\/12\/itviec-black-square-facebook.png","width":1800,"height":1800,"caption":"ITviec"},"image":{"@id":"https:\/\/itviec.com\/blog\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/ITviec","https:\/\/x.com\/ITviec","https:\/\/www.linkedin.com\/company\/itviec","https:\/\/www.youtube.com\/channel\/UCYthAQ3bcGr57M_ag5gHDvQ"]},{"@type":"Person","@id":"https:\/\/itviec.com\/blog\/#\/schema\/person\/1595d671c49cfa2a48cd3c0a047a1298","name":"Tien Tran","image":{"@type":"ImageObject","inLanguage":"vi","@id":"https:\/\/itviec.com\/blog\/wp-content\/uploads\/2024\/05\/tien-tran-author-e1715658627643-100x100.jpg","url":"https:\/\/itviec.com\/blog\/wp-content\/uploads\/2024\/05\/tien-tran-author-e1715658627643-100x100.jpg","contentUrl":"https:\/\/itviec.com\/blog\/wp-content\/uploads\/2024\/05\/tien-tran-author-e1715658627643-100x100.jpg","caption":"Tien Tran"},"url":"https:\/\/itviec.com\/blog\/author\/tien-tran\/"}]}},"_links":{"self":[{"href":"https:\/\/itviec.com\/blog\/wp-json\/wp\/v2\/posts\/88809","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/itviec.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/itviec.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/itviec.com\/blog\/wp-json\/wp\/v2\/users\/203"}],"replies":[{"embeddable":true,"href":"https:\/\/itviec.com\/blog\/wp-json\/wp\/v2\/comments?post=88809"}],"version-history":[{"count":4,"href":"https:\/\/itviec.com\/blog\/wp-json\/wp\/v2\/posts\/88809\/revisions"}],"predecessor-version":[{"id":89030,"href":"https:\/\/itviec.com\/blog\/wp-json\/wp\/v2\/posts\/88809\/revisions\/89030"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/itviec.com\/blog\/wp-json\/wp\/v2\/media\/89029"}],"wp:attachment":[{"href":"https:\/\/itviec.com\/blog\/wp-json\/wp\/v2\/media?parent=88809"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/itviec.com\/blog\/wp-json\/wp\/v2\/categories?post=88809"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/itviec.com\/blog\/wp-json\/wp\/v2\/tags?post=88809"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}