my-batis-seperator-useful-and-dangerous

Mybatis separator – tiện lợi và nguy hiểm

DB

Trước khi bắt đầu tìm hiểu Mybatis separator là gì?. Nếu yếu sinh lí, ý lộn, nếu chưa biết về Mybatis thì các ông có thể đọc bài giới thiệu về Mybatis ở Kieblog.

Tựa đề bài viết là Mybatis – chim nhỏ nhưng làm được lâu!. Đọc thôi là đã thấy cường dương bổ thận như rocket 1h rồi.

Trong quá trình làm việc tại công ty, gặp phải một dự án sử dụng Mybatis. Va vấp khá nhiều, rút ra không ít kinh nghiệm khi làm việc với chim nhỏ, nên viết ra bài này chia sẻ chút kinh nghiệm nhỏ nhoi khi làm việc với Mybatis separator.

Mong anh em đón đọc, có gì chưa đúng xin vui lòng bình luận phía dưới. Đm, tôi thề là ông nào chửi tôi cũng rep lại lịch sự nhé!.

mybatis seperator
Nhìn quen không?. Dynamic với Mybatis rõ ràng render động giấu phẩy giữa các điều kiện IN

BẮT ĐẦU NHA!

1 Mybatis separator là gì?

Trường hợp sử dụng foreach, seperator giúp thêm các dấu phân tách khi thực hiện loop bằng foreach. Cùng xem xét trường hợp dưới đây.

<select id="searchCoursesByTutors" parameterType="map"  resultMap="CourseResult">  
  SELECT * FROM COURSES  
  <if test="tutorIds != null">    
      <where>      
          tutor_id IN      
          <foreach item="tutorId" collection="tutorIds"         open="(" separator="," close=")">        
              #{tutorId}      
          </foreach>    
      </where>  
  </if> 
</select>

Trong trường hợp này seperator giúp thêm các dấu phẩy giữa các điều kiện IN. SQL sẽ generate ra như sau:

SELECT * FROM COURSES  
WHERE tutor_id IN (1,2,3)

Không phải chỉ mỗi cho các điều kiện WHERE IN, seperator còn hỗ trợ loop đối với các câu SQL cơ bản. các cặp value phức tạp hơn

SELECT * FROM COURSES  
WHERE (tutor_id, lession_id) IN ((1, 2), (2,3), (3,4))

2. Kinh nghiệm xương máu

Sử dụng Mybatis separator thật sự tiện lợi. Nói thẳng ra nhắc tới Dynamic SQL mà không có ví dụ về Mybatis separator thì cũng hết cả vui, hết cả sinh động.

Tuy nhiên, thực tế cho thấy, cần phải thật sự cẩn thận khi sử dụng separator.

Chuyện thế này, ở công ty, khi cả team tôi đang phát hục mặt làm task cho kịp tiến độ thì QC hét lên.

Ủa, sao cái bảng ABC này có tới cả triệu dòng thế?. Ai đã làm gì?. Mới chạy test lòng vòng có vài lần. Sao dữ liệu đâu ra lắm thế?.

Chả có gì để nói nếu tôi không phải là người vô tình tìm ra cái bug do vô tình mà có này.

Nói là bug vô tình nhưng nếu cứ để vậy mà release thì bug này không phải chuyện nhỏ. Tất cả các câu SQL viết cho dự án đều được yêu cầu tối ưu triệt để. (Anh em nào chưa biết thì đọc qua bài viết này Tối ưu SQL, jouneu to the west)

Dự án thì đang dùng Amazon EC3, nếu mới trong một thời gian ngắn sử dụng mà đã có tới mấy chục triệu row trong table thì thật là lạ (dự án đâu làm về big data)

Cắm đầu nhìn code, ban đầu thì chả có gì đặc biệt, anh đồng nghiệp insert một list data. Tất nhiên ảnh sẽ dùng foreach của separator, truyền vào một list, rồi cứ loop trên list đó để insert vào DB thôi.

Đại khái viết thế này:

// Tôi viết đại khái thôi nhé!. 
// Chứ theo policy công ty không thể public source được.
<foreach collection="danh_sach_group" item="group" separator="UNION">
  <foreach collection="group" item="model">
    insert into BANG_NAO_DO
    (ten, tuoi)
    values   
    (#{model.ten}, #{model.tuoi})
  </foreach>
</foreach>

3. Vấn đề ở đâu?

Như cái ví dụ phía trên tôi post lên thì các ông thấy vấn đề nằm ở đâu không?.

Vấn đề nằm ở chỗ cái separator=”UNION” đấy, thật sự tai hại. Nói không chắc một số anh em chưa hiểu được (cái Dynamic SQL thật sự cần trí tưởng tượng một chút).

Tôi lấy ví dụ nhé:

// Công ty có 2 group (A và B)
Group A - 2 thằng (Tên TÈO - 11 tuổi, tên TỒ - 12 tuổi)
Group B - 3 thằng (Tên TÍ - 13 tuổi, tên TỦN - 14 tuổi, tên TỊT - 15 tuổi)

// Với 2 group này, ta cần 5 câu insert vào BANG_NAO_DO

INSERT INTO BANG_NAO_DO (ten, tuoi) VALUES (TEO, 11)
INSERT INTO BANG_NAO_DO (ten, tuoi) VALUES (TO, 12)
INSERT INTO BANG_NAO_DO (ten, tuoi) VALUES (TI, 13)
INSERT INTO BANG_NAO_DO (ten, tuoi) VALUES (TUN, 14)
INSERT INTO BANG_NAO_DO (ten, tuoi) VALUES (TIT, 15)

Đấy, yêu cầu dùng Mybatis separator chỉ có thế. Insert 5 thằng trong 2 group vào là ổn, nhưng đây lại insert tới gấp đôi, gấp ba. Bảo sao database không quá tải!.

Ra production kiểu này là toang, dễ đi lắm, dễ ăn chửi lắm!. Nên anh em nhớ cẩn thận nha

4. Kết luận

Dynamic SQL thật sự rất tiện lợi, từ khóa Separator cũng vậy. Trường hợp sử dụng lịnh hoạt, SQL sẽ làm rất nhiều việc, bớt phải code ở phần logic Java.

Tuy nhiên, trường hợp sử dụng cần đặc biệt chú ý các từ khóa foreach, separator by UNION, OR, AND. Trước khi viết cần lường được trước câu SQL sẽ render như thế nào?.

Mybatis separator
Nhớ lường trước cái nguy hiểm của seperator UNION trước khi bắt đầu nha!

Nếu không thể lường trước, phải cố gắng show log câu SQL được render ra như thế nào rồi run thử. Tránh các trường hợp SQLException khi chạy production thực tế.

5. Tham khảo

Cảm ơn anh em đã đọc bài. Nhớ like và share bài viết nha. HAPPY CODING.

Có gì thắc mắc cứ comment đây nha! - Please feel free to comment here!
Chia sẻ bài viết