Giới thiệu
Thời gian tới ở công ty mình có về 1 dự án web sử dụng ngôn ngữ kotlin, xuất thân từ 1 dev java và có thời gian dài làm việc với php thì mình được sếp giao cho tìm hiểu về ngôn ngữ kotlin để chuẩn bị cho dự án mới, tranh thủ vọc vạch được chút nên mình viết bài này để chia sẻ lại cho mọi người
- Kotlin – phiên bản nâng cấp của Java: bắt đầu được phát triển vào năm 2010 bởi 1 công ty rất nổi tiếng về các IDE là JetBrains
- Học hỏi nhiều từ những ngôn ngữ khác, là tập hợp của những cái hay, cái mới nhất của những ngôn ngữ đó như: java, python, swift, javascript, …
- Tại sự kiện
Google I/O 2017
kotlin đã được Google thông báo làAndroid official language
- Ngoài việc hỗ trợ mạnh cho android ra thì kotlin còn hỗ trợ các nền tảng khác như:
mobile cross-platform
,native
,data science
,server-side
,web development
- Trong bài này thì mình sẽ hướng dẫn mọi người bắt đầu với nền tảng
server-side
- Ở trên tại sao mình lại nói “kotlin là phiên bản nâng cấp của java” bởi kotlin sử dụng các thư viện của java để phát triển (1 số thư viện hiện nay đã phát triển bằng kotlin) và chạy trên máy ảo java (jvm), vì vậy trong code kotlin thì chúng ta có thể sử dụng java và ngược lại
- Theo nhận định thì kotlin được dự đoán sẽ thay thế hoàn toàn java trong tương lai
- Để thực hiện phát triển kotlin trên nền tảng
server-side
thì mình có sử dụng 1 framework phát trển java web khá nổi tiếng làspring
, hiện tại thì framework này cũng đã hỗ trợ kotlin, ngoài ra mọi người có thể sử dụng các framework khác như: Javalin, KTor, Spark, Vert
Cài đặt môi trường
IDE
:IntelliJ
CSDL
:MySQL
- Công cụ làm việc với API:
Postman
Cài đặt dự án
- Ấn vào link start.spring (mình đã chọn sẵn các dependencies cơ bản nếu thích mọi người có thể tùy chỉnh lại hoặc thêm vào file
build.gradle.kts
sau khi đã tải src code về) - Chọn
generate
để tải về sau đó giải nén, mở bằngIntelliJ
đã cài đặt ở trên và đợi choIDE
cài đặt xong các gói cần thiết
Kết nối MySQL
Tạo database crud_kotlin
sau đó mở file src/main/resources/application.properties và thêm vào nội dung bên dưới (sửa lại username và password nếu bạn không dùng tài khoản mặc định)
1 2 3 4 | spring<span class="token punctuation">.</span>datasource<span class="token punctuation">.</span>url<span class="token operator">=</span>jdbc<span class="token operator">:</span>mysql<span class="token operator">:</span><span class="token operator">/</span><span class="token operator">/</span>localhost<span class="token operator">:</span><span class="token number">3306</span><span class="token operator">/</span>crud_kotlin spring<span class="token punctuation">.</span>datasource<span class="token punctuation">.</span>username<span class="token operator">=</span>root spring<span class="token punctuation">.</span>datasource<span class="token punctuation">.</span>password<span class="token operator">=</span> |
Hello world
- Tạo class controller
HomeController
src/main/kotlin/com/example/crud_kotlin/HomeController.kt và thay nội dung bên dưới vào1234567891011121314<span class="token keyword">package</span> com<span class="token punctuation">.</span>example<span class="token punctuation">.</span>crud_kotlin<span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>web<span class="token punctuation">.</span>bind<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span>GetMapping<span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>web<span class="token punctuation">.</span>bind<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span>RestController<span class="token annotation builtin">@RestController</span><span class="token keyword">class</span> HomeController <span class="token punctuation">{</span><span class="token annotation builtin">@GetMapping</span><span class="token keyword">fun</span> <span class="token function">index</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">:</span> String <span class="token punctuation">{</span><span class="token keyword">return</span> <span class="token string">"Hello world"</span><span class="token punctuation">}</span><span class="token punctuation">}</span> - Mở file src/main/kotlin/com/example/crud_kotlin/CrudKotlinApplication.kt, ấn vào biểu tượng hình tam giác bên trái hàm main và chọn
Run 'CrudKotlinApplication'
để chạy ứng dụng, khi run nếu IDE yều cầu cài thêm 1 số thành phần để hỗ trợ việc build như JDK, JVM, JRE, …thì mọi người cứ thực hiện cài đặt thêm bình thường - Mặc định khi chạy sẽ dùng cổng 8080, nếu trên máy đang dùng cổng này rồi thì trên console của IDE sẽ báo lỗi thì mọi người mở file src/main/resources/application.properties để cấu hình lại cổng12server<span class="token punctuation">.</span>port<span class="token operator">=</span><span class="token number">9999</span>
- Sau đó truy cập vào địa chỉ để test: http://localhost:8080
- Trong quá trình code thì có 1 lưu ý mọi người phải nhớ là khi thực hiện sửa bất cứ đoạn code nào mà muốn trình duyệt nhận mới thì đều phải thực hiện
Run 'CrudKotlinApplication'
lại để IDE build lại code mới
CRUD
Model
- Mỗi model (entity) trong spring tương ứng với 1 table, mỗi thuộc tính tương ứng với 1 column trong database
- Để demo mình sẽ thiết kế bảng
user
bao gồm 3 cộtid, name, phone
- Tương ứng với bảng đã thiết kế ở trên thì mình sẽ tạo 1 model có tên là
User
và bao gồm 3 thuộc tính - Tạo class model
User
src/main/kotlin/com/example/crud_kotlin/User.kt và thay nội dung bên dưới vào12345678910111213<span class="token keyword">package</span> com<span class="token punctuation">.</span>example<span class="token punctuation">.</span>crud_kotlin<span class="token keyword">import</span> javax<span class="token punctuation">.</span>persistence<span class="token punctuation">.</span><span class="token operator">*</span><span class="token annotation builtin">@Entity</span><span class="token keyword">data</span> <span class="token keyword">class</span> <span class="token function">User</span><span class="token punctuation">(</span><span class="token annotation builtin">@Id</span><span class="token annotation builtin">@GeneratedValue</span><span class="token punctuation">(</span>strategy <span class="token operator">=</span> GenerationType<span class="token punctuation">.</span>IDENTITY<span class="token punctuation">)</span><span class="token keyword">val</span> id<span class="token operator">:</span> Long<span class="token operator">?</span> <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">,</span><span class="token keyword">val</span> name<span class="token operator">:</span> String<span class="token punctuation">,</span><span class="token keyword">val</span> phone<span class="token operator">:</span> Int<span class="token punctuation">)</span> - Mặc định trong spring không có cơ chế quản lý migration, mọi người sẽ tự tạo bảng thủ công hoặc dùng cơ chế tự động thêm-xoá bảng cột tương ứng với model khi khởi tạo ứng dụng
- Để bật cơ chế tự động thêm-xoá thì mọi người mở file src/main/resources/application.properties và thêm vào nội dung bên dưới12spring<span class="token punctuation">.</span>jpa<span class="token punctuation">.</span>hibernate<span class="token punctuation">.</span>ddl<span class="token operator">-</span>auto<span class="token operator">=</span>create<span class="token operator">-</span>drop
- Ngoài ra nếu mọi người không thích cơ chế tự động này của spring thì có thể cài thêm package flyway để quản lý lịch sử sửa đổi
Repository
- Ở bước trên mình đã tạo được bảng và model tương ứng, giờ chúng ta sẽ tạo
repository
để thực hiện truy vấn vàodatabase
, trong spring đã xây dựng các hàm cơ bản để thao tác với database nên mọi người sẽ extends interfaceJpaRepository
và truyền vào model tương ứng - Tạo interface repository
UserRepository
src/main/kotlin/com/example/crud_kotlin/UserRepository.kt và thay nội dung bên dưới vào12345678910<span class="token keyword">package</span> com<span class="token punctuation">.</span>example<span class="token punctuation">.</span>crud_kotlin<span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>data<span class="token punctuation">.</span>jpa<span class="token punctuation">.</span>repository<span class="token punctuation">.</span>JpaRepository<span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>stereotype<span class="token punctuation">.</span>Repository<span class="token keyword">import</span> javax<span class="token punctuation">.</span>transaction<span class="token punctuation">.</span>Transactional<span class="token annotation builtin">@Repository</span><span class="token annotation builtin">@Transactional</span><span class="token punctuation">(</span>Transactional<span class="token punctuation">.</span>TxType<span class="token punctuation">.</span>MANDATORY<span class="token punctuation">)</span><span class="token keyword">interface</span> UserRepository<span class="token operator">:</span> JpaRepository<span class="token operator"><</span>User<span class="token punctuation">,</span> Long<span class="token operator">></span> - Ngoài các hàm đã được hỗ trợ trong
JpaRepository
mọi người có thể khai báo các hàm tuỳ chỉnh ở đây theo cú phápJpaRepository
, phần này mọi người tìm hiểu thêm trong bài này mình chỉ demo các hàm cơ bản nên không cần khai báo thêm gì
Service
- Để xử lý logic trong ứng dụng cũng như lấy query dữ liệu thông qua repository mình sẽ tạo 1 class service
- Tạo class service
UserService
src/main/kotlin/com/example/crud_kotlin/UserService.kt và thay nội dung bên dưới vào123456789101112131415161718192021222324252627282930313233343536373839404142<span class="token keyword">package</span> com<span class="token punctuation">.</span>example<span class="token punctuation">.</span>crud_kotlin<span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>http<span class="token punctuation">.</span>HttpStatus<span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>http<span class="token punctuation">.</span>ResponseEntity<span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>stereotype<span class="token punctuation">.</span>Service<span class="token annotation builtin">@Service</span><span class="token keyword">class</span> <span class="token function">UserService</span><span class="token punctuation">(</span><span class="token keyword">private</span> <span class="token keyword">val</span> userRepository<span class="token operator">:</span> UserRepository<span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token keyword">fun</span> <span class="token function">index</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">:</span> List<span class="token operator"><</span>User<span class="token operator">></span> <span class="token punctuation">{</span><span class="token keyword">return</span> userRepository<span class="token punctuation">.</span><span class="token function">findAll</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token keyword">fun</span> <span class="token function">store</span><span class="token punctuation">(</span>user<span class="token operator">:</span> User<span class="token punctuation">)</span><span class="token operator">:</span> ResponseEntity<span class="token operator"><</span>User<span class="token operator">></span> <span class="token punctuation">{</span><span class="token keyword">return</span> ResponseEntity<span class="token punctuation">.</span><span class="token function">ok</span><span class="token punctuation">(</span>userRepository<span class="token punctuation">.</span><span class="token function">save</span><span class="token punctuation">(</span>user<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token keyword">fun</span> <span class="token function">show</span><span class="token punctuation">(</span>userId<span class="token operator">:</span> Long<span class="token punctuation">)</span><span class="token operator">:</span> ResponseEntity<span class="token operator"><</span>User<span class="token operator">></span> <span class="token punctuation">{</span><span class="token keyword">return</span> userRepository<span class="token punctuation">.</span><span class="token function">findById</span><span class="token punctuation">(</span>userId<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">map</span> <span class="token punctuation">{</span> user <span class="token operator">-></span>ResponseEntity<span class="token punctuation">.</span><span class="token function">ok</span><span class="token punctuation">(</span>user<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">.</span><span class="token function">orElse</span><span class="token punctuation">(</span>ResponseEntity<span class="token punctuation">.</span><span class="token function">notFound</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token keyword">fun</span> <span class="token function">update</span><span class="token punctuation">(</span>userId<span class="token operator">:</span> Long<span class="token punctuation">,</span> newUser<span class="token operator">:</span> User<span class="token punctuation">)</span><span class="token operator">:</span> ResponseEntity<span class="token operator"><</span>User<span class="token operator">></span> <span class="token punctuation">{</span><span class="token keyword">return</span> userRepository<span class="token punctuation">.</span><span class="token function">findById</span><span class="token punctuation">(</span>userId<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">map</span> <span class="token punctuation">{</span> currentUser <span class="token operator">-></span><span class="token keyword">val</span> updatedUser<span class="token operator">:</span> User <span class="token operator">=</span>currentUser<span class="token punctuation">.</span><span class="token function">copy</span><span class="token punctuation">(</span>name <span class="token operator">=</span> newUser<span class="token punctuation">.</span>name<span class="token punctuation">,</span>phone <span class="token operator">=</span> newUser<span class="token punctuation">.</span>phone<span class="token punctuation">)</span>ResponseEntity<span class="token punctuation">.</span><span class="token function">ok</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">body</span><span class="token punctuation">(</span>userRepository<span class="token punctuation">.</span><span class="token function">save</span><span class="token punctuation">(</span>updatedUser<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">.</span><span class="token function">orElse</span><span class="token punctuation">(</span>ResponseEntity<span class="token punctuation">.</span><span class="token function">notFound</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token keyword">fun</span> <span class="token function">destroy</span><span class="token punctuation">(</span>userId<span class="token operator">:</span> Long<span class="token punctuation">)</span><span class="token operator">:</span> ResponseEntity<span class="token operator"><</span>Void<span class="token operator">></span> <span class="token punctuation">{</span><span class="token keyword">return</span> userRepository<span class="token punctuation">.</span><span class="token function">findById</span><span class="token punctuation">(</span>userId<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">map</span> <span class="token punctuation">{</span> user <span class="token operator">-></span>userRepository<span class="token punctuation">.</span><span class="token function">delete</span><span class="token punctuation">(</span>user<span class="token punctuation">)</span>ResponseEntity<span class="token operator"><</span>Void<span class="token operator">></span><span class="token punctuation">(</span>HttpStatus<span class="token punctuation">.</span>ACCEPTED<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">.</span><span class="token function">orElse</span><span class="token punctuation">(</span>ResponseEntity<span class="token punctuation">.</span><span class="token function">notFound</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">}</span>
Controller
- Cuối cùng để định nghĩa các router và các đoạn xử lý tương ứng với router mình sẽ tạo 1 class controller, xử lý thì mình sẽ gọi từ controller vào service đã định nghĩa phía trên
- Phía dưới thì mình có định nghĩa router có dạng
/users
để thực hiện CRUD theo đúng chuẩnRESTful API
(nếu bạn nào chưa biết thì có thể google đọc thêm nhé ^^) - Tạo class controller
UserController
src/main/kotlin/com/example/crud_kotlin/UserController.kt và thay nội dung bên dưới vào1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859<span class="token keyword">package</span> com<span class="token punctuation">.</span>example<span class="token punctuation">.</span>crud_kotlin<span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>http<span class="token punctuation">.</span>ResponseEntity<span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>web<span class="token punctuation">.</span>bind<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span><span class="token operator">*</span><span class="token annotation builtin">@RestController</span><span class="token annotation builtin">@RequestMapping</span><span class="token punctuation">(</span><span class="token string">"users"</span><span class="token punctuation">)</span><span class="token keyword">class</span> <span class="token function">UserController</span><span class="token punctuation">(</span><span class="token keyword">private</span> <span class="token keyword">val</span> userService<span class="token operator">:</span> UserService<span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token comment">/*** Read*/</span><span class="token annotation builtin">@GetMapping</span><span class="token keyword">fun</span> <span class="token function">index</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">:</span> List<span class="token operator"><</span>User<span class="token operator">></span> <span class="token punctuation">{</span><span class="token keyword">return</span> userService<span class="token punctuation">.</span><span class="token function">index</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token comment">/*** Create*/</span><span class="token annotation builtin">@PostMapping</span><span class="token keyword">fun</span> <span class="token function">store</span><span class="token punctuation">(</span><span class="token annotation builtin">@RequestBody</span> user<span class="token operator">:</span> User<span class="token punctuation">)</span><span class="token operator">:</span> ResponseEntity<span class="token operator"><</span>User<span class="token operator">></span> <span class="token punctuation">{</span><span class="token keyword">return</span> userService<span class="token punctuation">.</span><span class="token function">store</span><span class="token punctuation">(</span>user<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token comment">/*** Read*/</span><span class="token annotation builtin">@GetMapping</span><span class="token punctuation">(</span><span class="token string">"/{id}"</span><span class="token punctuation">)</span><span class="token keyword">fun</span> <span class="token function">show</span><span class="token punctuation">(</span><span class="token annotation builtin">@PathVariable</span><span class="token punctuation">(</span>value <span class="token operator">=</span> <span class="token string">"id"</span><span class="token punctuation">)</span> userId<span class="token operator">:</span> Long<span class="token punctuation">)</span><span class="token operator">:</span> ResponseEntity<span class="token operator"><</span>User<span class="token operator">></span> <span class="token punctuation">{</span><span class="token keyword">return</span> userService<span class="token punctuation">.</span><span class="token function">show</span><span class="token punctuation">(</span>userId<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token comment">/*** Update*/</span><span class="token annotation builtin">@PutMapping</span><span class="token punctuation">(</span><span class="token string">"/{id}"</span><span class="token punctuation">)</span><span class="token keyword">fun</span> <span class="token function">update</span><span class="token punctuation">(</span><span class="token annotation builtin">@PathVariable</span><span class="token punctuation">(</span>value <span class="token operator">=</span> <span class="token string">"id"</span><span class="token punctuation">)</span> userId<span class="token operator">:</span> Long<span class="token punctuation">,</span><span class="token annotation builtin">@RequestBody</span> newUser<span class="token operator">:</span> User<span class="token punctuation">)</span><span class="token operator">:</span> ResponseEntity<span class="token operator"><</span>User<span class="token operator">></span> <span class="token punctuation">{</span><span class="token keyword">return</span> userService<span class="token punctuation">.</span><span class="token function">update</span><span class="token punctuation">(</span>userId<span class="token punctuation">,</span> newUser<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token comment">/*** Delete*/</span><span class="token annotation builtin">@DeleteMapping</span><span class="token punctuation">(</span><span class="token string">"/{id}"</span><span class="token punctuation">)</span><span class="token keyword">fun</span> <span class="token function">destroy</span><span class="token punctuation">(</span><span class="token annotation builtin">@PathVariable</span><span class="token punctuation">(</span>value <span class="token operator">=</span> <span class="token string">"id"</span><span class="token punctuation">)</span> userId<span class="token operator">:</span> Long<span class="token punctuation">)</span><span class="token operator">:</span> ResponseEntity<span class="token operator"><</span>Void<span class="token operator">></span> <span class="token punctuation">{</span><span class="token keyword">return</span> userService<span class="token punctuation">.</span><span class="token function">destroy</span><span class="token punctuation">(</span>userId<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">}</span> - Test thì mọi người có thể sử dụng tool
postman
để thao tác và truyền dữ liệu qua đường dẫn/users
theo đúng chuẩnRESTful API
, phần này chắc anh em làm web biết rồi nên mình xin phép không dài dòng ^^
Đọc thêm
- Java to kotlin: https://fabiomsr.github.io/from-java-to-kotlin
- Migration: flyway
- Authorization: spring security + jwt
- Template engine: mustache