跳到主要內容

Spring boot v1.5 (五) JDBCTemplate

    我個人是較推崇 JPA的,不過有些情況下,或是架構設計上,會需要純SQL的方式進行,但是像教科書介紹的方式進行,在實務上很難看到,太難用、程式太醜、修改太複雜等等都是要克服的問題,不可能期望所有開發者都是同一高等級的,JDBCTemplate某程度簡化了使用SQL的繁雜度,若是需要大量的純SQL時,JDBCTemplate絕對是首選,也會介紹JDBCTemplate如增刪修查方式、Connection Pool設定及帳密加密的方式等等。

  • 預先準備項目:請先下載安裝 PostgreSQL,並建立一個DB(louisz)及Table(users),DDL如下:
CREATE DATABASE louisz
    WITH 
    OWNER = louisz
    ENCODING = 'UTF8'
    LC_COLLATE = 'Chinese (Traditional)_Taiwan.950'
    LC_CTYPE = 'Chinese (Traditional)_Taiwan.950'
    TABLESPACE = pg_default
    CONNECTION LIMIT = -1;

CREATE TABLE public.users
(
    id integer NOT NULL,
    name character varying COLLATE pg_catalog."default",
    email character varying COLLATE pg_catalog."default",
    mobilenumber character varying COLLATE pg_catalog."default",
    CONSTRAINT user_pkey PRIMARY KEY (id)
)
WITH (
    OIDS = FALSE
)
TABLESPACE pg_default;

學習目的:使用自訂屬性設定檔、屬性一些進階的使用。
學習時數:3.5hr
教學影片:

pom.xml 說明
spring-boot-starter-web:配置 Web Project所需的函式庫。
spring-boot-starter-test:配置 unit or mock test 所需的函式庫。
spring-boot-starter-actuator:配置監控spring boot所需的函式庫,後續spring cloud會使用到,所以一開就導入。
spring-boot-starter-jdbc:配置使用jdbc所需的函式庫。
postgresql:配置postgresql連接Driver所需的函式庫。
jasypt-spring-boot-starter:加解密所需的函式庫。
example5.properties JDBC設定檔說明
#DB設定資訊
#PostgreSQL JDBC URL
spring.datasource.url= jdbc:postgresql://localhost:5432/louisz
#DB使用者帳號
spring.datasource.username=louisz
#DB使用者密碼已經加過密的,請使用自己產生的
spring.datasource.password=ENC(xOSgfOF9jzB8jpYSB1O+4jEobziG16pA)

產生亂碼值的指令
java -cp ~/.m2/repository/org/jasypt/jasypt/1.9.2/jasypt-1.9.2.jar  org.jasypt.intf.cli.JasyptPBEStringEncryptionCLI input="contactspassword" password=supersecretz algorithm=PBEWithMD5AndDES
input:需要加密的資訊
password:壓密KEY,可以自己定義
結果應該像是這樣,OUTPUT就是被加密過後的值

----ENVIRONMENT-----------------

Runtime: Oracle Corporation Java HotSpot(TM) 64-Bit Server VM 24.45-b08



----ARGUMENTS-------------------

algorithm: PBEWithMD5AndDES
input: contactspassword
password: supersecretz



----OUTPUT----------------------

XcBjfjDDjxeyFBoaEPhG14wEzc6Ja+Xx+hNPrJyQT88=

程式碼說明
  • Example5.java
package louisz.springboot.example5;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.PropertySource;

/**
 * Louisz Spring boot introduce ex.5
 *
 */

@SpringBootApplication
@PropertySource(name = "EncryptedProperties", value = "classpath:example5.properties")//針對需加解密的設定檔檔名及位置
public class Example5 {
 /**
  * 程式執行起點
  * 
  * @param args
  */
 public static void main(String[] args) {
  SpringApplication.run(Example5.class, args);

 }

}
  • UserService.java
package louisz.springboot.example5;

import java.util.List;

import louisz.springboot.example3.User;

public interface UserService {
 /**
  * 新增一筆User資料
  * 
  * @param User
  */
 void merge(User user);

 /**
  * 刪除傳入相同姓名的資料
  * 
  * @param name
  */
 void deleteByName(String name);

 /**
  * 查詢所有資料,並回傳List物件
  */
 List listAll();

 /**
  * 查詢特定id資料,並回該User Object
  */
 
 User findById(Long id);
 
 /**
  * 删除所有用户
  */
 void deleteAllUsers();

 /**
  * 依據傳入的id,依據傳入的User物件,更新資料表中相同id的資料
  * 
  * @param id
  * @param user
  */
 void updateUser(Long id, User user);
}

  • UserServiceImpl.java
package louisz.springboot.example5;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;

import louisz.springboot.example3.User;

@Service
public class UserServiceImpl implements UserService {

 @Autowired
 private JdbcTemplate jdbcTemplate;

 @Override
 public void merge(User user) {
  jdbcTemplate.update("insert into users(id,name,email,mobilenumber) values(?, ?,?,?)", user.getId(),
    user.getName(), user.getEmail(), user.getMobilenumber());
 }

 @Override
 public void deleteByName(String name) {
  jdbcTemplate.update("delete from users where name = ?", name);
 }

 @Override
 public List listAll() {
  return jdbcTemplate.query("select * from users", new BeanPropertyRowMapper(User.class));
 }

 @Override
 public void deleteAllUsers() {
  jdbcTemplate.update("delete from users ");
 }

 @Override
 public void updateUser(Long id, User user) {
  jdbcTemplate.update("update users set name = ? where id = ?", user.getName(), id);
 }

 @Override
 public User findById(Long id) {
  return (User)jdbcTemplate.queryForObject(
    "select * from users where id = ?", new Object[] { id },
    new BeanPropertyRowMapper(User.class));
 }

}

  • TestUserServiceImpl.java
package louisz.springboot.example5;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.test.context.junit4.SpringRunner;

import louisz.springboot.example3.User;

//spring boot 1.4以後的方式
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class TestUserServiceImpl {

 @Autowired
 private UserService userSerivce;// 注入UserService
         // interface,實際instance會呼叫UserServiceImpl物件

 @Before
 public void setUp() {
  // 先清除所有資料
  userSerivce.deleteAllUsers();
 }

 @Test
 public void test() throws Exception {
  /*
   * 進行單元測試,先規劃相關情境
   */
  User user1 = new User();
  user1.setId(1l);
  user1.setEmail("louisz6ster@gmail.com");
  user1.setName("user1");
  user1.setMobilenumber("0912345678");

  User user2 = new User();
  user2.setId(2l);
  user2.setEmail("louisz6ster@gmail.com");
  user2.setName("user2");
  user2.setMobilenumber("0912345678");

  User user3 = new User();
  user3.setId(3l);
  user3.setEmail("louisz6ster@gmail.com");
  user3.setName("user3");
  user3.setMobilenumber("0912345678");
  // 新增三個User資料
  userSerivce.merge(user1);
  userSerivce.merge(user2);
  userSerivce.merge(user3);

  // 查詢全部資料筆數是否吻合,確認新增成功
  Assert.assertEquals(3, userSerivce.listAll().size());
  // 刪除資料
  userSerivce.deleteByName("user1");
  userSerivce.deleteByName("user2");
  // 查詢全部資料筆數是否吻合,確認刪除成功
  Assert.assertEquals(1, userSerivce.listAll().size());
  
  //更新User3姓名跟email
  user3.setName("Jessie");
  userSerivce.updateUser(user3.getId(), user3);
  
  //查詢資料表中User3的資料是否更新成功
  User newUser3 = userSerivce.findById(user3.getId());
  Assert.assertEquals(user3.getName(), newUser3.getName());
  
 }

}

單元測試看結果,執行時請加上-Djasypt.encryptor.password=supersecretz
Github code(louisz.springboot.example5)
參考連結:
http://blog.didispace.com/springbootdata1/
https://www.ricston.com/blog/encrypting-properties-in-spring-boot-with-jasypt-spring-boot/
https://github.com/ulisesbocchio/jasypt-spring-boot


留言

這個網誌中的熱門文章

Spring boot v1.5 (六) spring data jpa 基本操作

最近天氣好熱,做甚麼事都覺得很懶,想要寫個spring data jpa也是懶懶的,不過這部分卻也是滿重要的一部分,前一篇介紹 JDBCTemplate ,已經覺得跟以前寫SQL方式有所差異了,JPA帶來的是物件導向的設計面思考,說到JPA不得不提提 ORM ,Object-relational mapping主要想法為簡化及物件導向的設計,讓RDB更貼近Object,在設計上可以更加便利,甚至透過一些設計可以讓Table具有物件導向的特性如繼承等等,以往要使用ORM的框架,都會先以 Hibernate 進行,不過近來慢慢地轉向JPA,主要還是在減少程式碼、增加彈性等等,大體的功能沒有差異很大,所以從Hibernate轉到JPA問題不大,JPA要介紹的東西還滿多的,所以我這裡會再分成三個章節來介紹。 SPRING DATA JPA基本操作 JPQL & Named SQL & Native SQL Cache & DB Design Pattern SPRING DATA JPA更加簡化的程式撰寫,只需要一個 Interface內寫一些查詢 method就可以操作JPA,因為利用 method 組合查詢條件,確實很方便也很容易理解,若是都沒有辦法符合需求當然也可以自己實作一個來用當然沒有問題。 學習目的 :SPRING DATA JPA基本操作。 學習時數 :3.5hr 教學影片: pom.xml 說明 spring-boot-starter-web:配置 Web Project所需的函式庫。 spring-boot-starter-test:配置 unit or mock test 所需的函式庫。 spring-boot-starter-actuator:配置監控spring boot所需的函式庫,後續spring cloud會使用到,所以一開就導入。 spring-boot-starter-jdbc:配置使用jdbc所需的函式庫。 postgresql:配置postgresql連接Driver所需的函式庫。 jasypt-spring-boot-starter:加解密所需的函式庫。 spring-boot-starter-data-jpa:配置Spring data jpa所需的函式庫。 ...

Pentaho kettle取指定目錄下的所有檔案

最近開始玩 BI的東西,而之前專案有使用到Pentaho Open Source這個好物.... Pentaho裡面很多東西,跟 Jasperreport一樣東西很多,我最先接觸到的是kettle ETL的工具,玩了一陣子,開始有空就把它寫下來,以免忘記。 Scan一個目錄下所有檔案,然後塞進去資料庫 1.先拉兩個 Input,一個Get File Names,一個是CSV file input,再拉一個 output 中的 Table output,然後把他連起來。 2.點開 Get File Names,File or directory設定你的指定目錄,Regular Expression則是輸入.*\.*$則是所有檔案,若是CSV則可.*\.torrent$這可以了,可以按一下 Preview rows看看是否正確。

IReport中的條碼類別BcImage

最近有一個繳費單的專案,需要列印條碼,因為IReport本身就有提供列印條碼的功能,所以就用IReport設計繳費單然後再用Jasperreport API寫批次程式去產生PDF,資料量大概3000多筆(頁)拆成幾個檔案,可是就發生了一個問題居然發生部分繳費單的條碼有問題,看了一下程式研判是Race Condition因為程式採用的是多執行緒,若是依序個別產生是不會有問題,但是同時執行的時候就會亂掉,而且都錯在條碼,令我覺得很奇怪,後來我查了一下發現it.businesslogic.ireport.barcode.BcImage 類別我猜是這裡的問題,就先加上synchronized重新編譯在去執行,嘿嘿條碼就對了,做了一下壓測也正常,程式碼如下,不過這樣事不是對了可能還要仔細查查看。 public class BcImage { private static net.sourceforge.barbecue.Barcode bc = null; public synchronized static net.sourceforge.barbecue.Barcode getBarcode() { return bc; } public synchronized static BufferedImage getBarcodeImage(int type, Object aText, boolean showText, boolean checkSum) { return getBarcodeImage(type, aText, showText, checkSum,"",0,0); } public synchronized static BufferedImage getBarcodeImage(int type, Object aText, boolean showText, boolean checkSum, String applicationIdentifier, int width, int height) { // 2of7, 3of9, Bookland, Codabar, Code128,...