HttpClient Service - Spring Boot 168 EP 22-6

HttpClient Service – Spring Boot 168 EP 22-6

建立第三方 API 的 Model、Repository 後,採分層架構設計,區分為表現層、邏輯層、及資料層,Service 的應用在於邏輯層,每層都有獨立職責,多層協同提供完整的功能,使用 JPA 增加範例 ,並透過 JUnit 5 來驗證產出結果。

前言

Spring Data JPA 是基於 Hibernate 開發的 JPA 框架,簡化了 JPA 的寫法,可以在幾乎不用寫實作程式碼的情況下,實現對資料庫的存取操作,也能設定多個資料庫來源,使用多個資料表查詢,採用原生 SQL 自訂查詢等功能。

Java HttpClient Service

檔案目錄

./
   +- build.gradle
       +- src
           +- main
               +- resources
               |   +- application.properties
               +- java
                   +- org
                       +- ruoxue
                           +- spring_boot_168
                               +- game
                               |   +- ggg
                               |       +- service
                               |       |   +- UserService.java 
                               |       |   +- UserServiceImpl.java 

設定

UserRepository.java

Repository 如何建立,參考此篇:

實作

UserService.java

建立 Service 介面,宣告 CRUD 方法,此時並沒有任何實作。

package org.ruoxue.spring_boot_168.game.ggg.service;

import org.ruoxue.spring_boot_168.game.ggg.model.User;

/**
 * 使用者服務
 */
public interface UserService {

	/**
	 * 新增使用者
	 * 
	 * @param user
	 * @return
	 */
	User insert(User user);

	/**
	 * 依帳號取得使用者
	 * 
	 * @param cid
	 * @return
	 */
	User findByCid(String cid);

	/**
	 * 更新使用者
	 * 
	 * @param user
	 * @return
	 */
	User update(User user);

	/**
	 * 刪除使用者
	 * 
	 * @param user
	 */
	void delete(User user);
}

UserServiceImpl.java

實作 Service ,注入 UserRepository ,加上 @Transactional 實現交易機制。

package org.ruoxue.spring_boot_168.game.ggg.service;

import org.ruoxue.spring_boot_168.game.ggg.model.User;
import org.ruoxue.spring_boot_168.game.ggg.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * 使用者服務實作
 */
@Service
public class UserServiceImpl implements UserService {

	/**
	 * 使用者儲存庫
	 */
	@Autowired
	private UserRepository userRepository;

	public UserServiceImpl() {
	}

	/**
	 * 新增使用者
	 * 
	 * @param user
	 * @return
	 */
	@Transactional
	@Override
	public User insert(User user) {
		User result = userRepository.save(user);
		return result;
	}

	/**
	 * 依帳號取得使用者
	 * 
	 * @param cid
	 * @return
	 */
	@Override
	public User findByCid(String cid) {
		User result = userRepository.findByCid(cid);
		return result;
	}

	/**
	 * 更新使用者
	 * 
	 * @param user
	 * @return
	 */
	@Transactional
	@Override
	public User update(User user) {
		User result = userRepository.save(user);
		return result;
	}

	/**
	 * 刪除使用者
	 * 
	 * @param user
	 */
	@Transactional
	@Override
	public void delete(User user) {
		userRepository.delete(user);
	}
}

測試 JUnit 5

UserServiceImplTest.java

新增單元測試,驗證是否符合預期 。

{

	@Autowired
	private UserService service;

	@Test
	public void service() {
		System.out.println(service);
		assertNotNull(service);
	}

	@Test
	public void insert() {
		String value = "ruoxue";
		User found = service.findByCid(value);
		if (found != null) {
			return;
		}

		User user = new User();
		user.setCid(value);
		user.setName("player");
		user.setPassword("");
		user.setSalt("");
		User result = service.insert(user);
		System.out.println(result);
		assertNotNull(result);
		assertEquals(value, result.getCid());
	}

	@Test
	public void findByCid() {
		String value = "ruoxue";
		User user = service.findByCid(value);
		System.out.println(user);
		if (user != null) {
			assertEquals(value, user.getCid());
		}
	}

	@Test
	public void update() {
		String value = "ruoxue";
		String name = "test_player";
		User user = service.findByCid(value);
		if (user != null) {
			user.setName(name);
			User updated = service.update(user);
			System.out.println(user);
			assertEquals(name, updated.getName());
		}
	}

	@Test
	public void delete() {
		String value = "ruoxue";
		User user = service.findByCid(value);
		if (user != null) {
			service.delete(user);
			System.out.println(user);
		}
	}
}

insert

測試方法上點右鍵執行 Run As -> JUnit Test ,查看 console。

Hibernate: select user0_.id as id1_0_, user0_.cid as cid2_0_, user0_.name as name3_0_, user0_.password as password4_0_, user0_.salt as salt5_0_ from game_ggg_user user0_ where user0_.cid=?
Hibernate: insert into game_ggg_user (cid, name, password, salt, id) values (?, ?, ?, ?, ?)
{"cid":"ruoxue","name":"player"}

findByCid

測試方法上點右鍵執行 Run As -> JUnit Test ,查看 console。

Hibernate: select user0_.id as id1_0_, user0_.cid as cid2_0_, user0_.name as name3_0_, user0_.password as password4_0_, user0_.salt as salt5_0_ from game_ggg_user user0_ where user0_.cid=?
{"cid":"ruoxue","name":"player"}    

update

測試方法上點右鍵執行 Run As -> JUnit Test ,查看 console。

Hibernate: select user0_.id as id1_0_, user0_.cid as cid2_0_, user0_.name as name3_0_, user0_.password as password4_0_, user0_.salt as salt5_0_ from game_ggg_user user0_ where user0_.cid=?
Hibernate: select user0_.id as id1_0_0_, user0_.cid as cid2_0_0_, user0_.name as name3_0_0_, user0_.password as password4_0_0_, user0_.salt as salt5_0_0_ from game_ggg_user user0_ where user0_.id=?
Hibernate: update game_ggg_user set cid=?, name=?, password=?, salt=? where id=?
{"cid":"ruoxue","name":"test_player"}

delete

測試方法上點右鍵執行 Run As -> JUnit Test ,查看 console。

Hibernate: select user0_.id as id1_0_, user0_.cid as cid2_0_, user0_.name as name3_0_, user0_.password as password4_0_, user0_.salt as salt5_0_ from game_ggg_user user0_ where user0_.cid=?
Hibernate: select user0_.id as id1_0_0_, user0_.cid as cid2_0_0_, user0_.name as name3_0_0_, user0_.password as password4_0_0_, user0_.salt as salt5_0_0_ from game_ggg_user user0_ where user0_.id=?
Hibernate: delete from game_ggg_user where id=?
{"cid":"ruoxue","name":"test_player"}

HeidiSQL

查看資料表結構,欄位名稱、資料類型、長度定義等。

HeidiSQL 查看資料表結構

查看新增資料,PK 、帳號、名稱等。

HeidiSQL 查看資料

心得分享

邏輯層 Service ,使用 Repository 存取資料庫,並加上 @Transactional 交易機制,實現資料庫 commit、rollback 功能,目前是單筆資料的儲存,日後當擴展到多個 Repository 更新多筆資料一次交易時,有交易機制的實現,才能保證資料的完整正確性。

發佈留言