Day 12 - Spring Boot & MyBatis

MyBatis 可以简单的使用注解XML 的方式进行配置和对映,通过将引数对映到配置的SQL 形成最终执行的SQL 语法最後将SQL 语法执行结果对映成Java 物件返回

在开始实作功能以前,我们会先简单介绍一下ORM 以及它的优缺点,再来展示如何实作注解及XML 两种使用方式,而实作的功能与上一篇Day 11 - Spring Boot & JdbcTemplate一样。

ORM

ORM,全名为Object Relational Mapping,中文名称为物件关系对映目的是为了解决面向物件与关联式资料库中资料类型互不匹配的问题,它的作法是在实体物件和关联式资料库之间做一个映射,这样在操作实体物件时就不需要使用复杂的SQL 语法,只需要操作物件的属性和方法。

优点

  1. 解决操作资料库的重复性
    我们对资料库的操作无非就是增、删、改、查四种操作,但当我们在实作持久层时,我们还是需要写大量的程序码,只因为我们操作的资料表不同,而使用ORM 则可以自动生成CRUD 语法,开发时就可以把更多的精力放在业务逻辑上。

  2. 增加应用程序的扩展性和灵活性并防止SQL 注入
    当我们在编写持久层逻辑时,如果操作的是MySQL 资料库时,我们就会使用相应的语法以及配置,但当我们需要更换资料库时,整个持久层的逻辑就需要重新编写。

    而ORM 做到了实体物件与资料库之间的映射,ORM 可以通过映射关系自动生成SQL 语法,因此不必再编写重复的程序码且不用担心SQL 注入的问题。

缺点

  1. 自动化进行实体物件与资料库之间的映射代价就是消耗系统性能
  2. 在处理多表查询或是where 条件复杂查询时,ORM 语法会变得更加复杂。

MyBatis 常用注释

CRUD 注释

  • @Insert : 用於新增叙述。
  • @Select : 用於查询叙述。
  • @Update : 用於修改叙述。
  • @Delete : 用於删除叙述。
@Mapper
public class DemoUserMapper {

	@Insert("INSERT INTO user(name, age) VALUES(#{name}, #{age})")
	public int add(User user);

	@Select("SELECT * FROM user")
	public List<User> getList();

	@Select("SELECT * FROM user WHERE id = #{id}")
	public User getDataById(String id);

	@Update("UPDATE user SET name = #{name}, age = #{age} WHERE id = #{id}")
	public int updateDataById(User user);

	@Delete("DELETE FROM user Where id = #{id}")
	public int deleteDataById(String id);

}

对映注释

  • @Results : 用於多个栏位的对映关系。
  • @Result : 用於单一栏位的对映关系。
  • @ResultMap : 根据ID 连结XML 的。
@Results({
	@Result(property = "username", column = "USERNAME"),
	@Result(property = "password", column = "PASSWORD"),
})
@Selete("SELECT * FROM user")
public List<User> getList();

注解实作方式

新增依赖

<!-- MyBatis -->
<dependency>
	<groupId>org.mybatis.spring.boot</groupId>
	<artifactId>mybatis-spring-boot-starter</artifactId>
	<version>2.0.1</version>
</dependency>

建立持久层接口

package com.example.demo.mapper;

import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;

import com.example.demo.entity.MemberAccount;

@Mapper
public interface MemberAccountMapper {

	@Insert(" INSERT INTO test_project.member_account ( "
          + "	   USERNAME, PASSWORD, SALT, "
		  + "	   CREATE_BY, CREATE_TIME, UPDATE_BY, UPDATE_TIME "
		  + " ) "
		  + " VALUE ( "
		  + "	   #{username}, #{password}, #{salt}, "
		  + "	   #{create_by}, NOW(), #{update_by}, NOW() "
		  + " ) ")
	public Integer insert(MemberAccount memberAccount);
	
	@Select(" SELECT "
		  + "	   ID, USERNAME, PASSWORD, SALT "
		  + " FROM "
		  + "	   test_project.member_account "
		  + " WHERE "
		  + "	   USERNAME = #{username} ")
	public MemberAccount findMemberAccountByUsername(String username);
	
	@Update(" UPDATE "
		  + "	   test_project.member_account "
		  + " SET "
		  + "	   PASSWORD = #{password}, UPDATE_BY = #{update_by}, UPDATE_TIME = NOW() "
		  + " WHERE "
		  + "	   ID = #{id} ")
	public Integer update(MemberAccount memberAccount);
	
}

测试功能

package com.example.demo.mapper;

import java.util.UUID;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import com.example.demo.entity.MemberAccount;

@SpringBootTest
public class MemberAccountMapperTest {

	@Autowired
	private MemberAccountMapper memberAccountMapper;
	
	@Test
	public void insert() {
		MemberAccount memberAccount = new MemberAccount();
		memberAccount.setUsername("[email protected]");
		memberAccount.setPassword("password");

		String salt = UUID.randomUUID().toString().toUpperCase().replaceAll("-", "");
		memberAccount.setSalt(salt);
		memberAccount.setCreate_by(memberAccount.getUsername());
		memberAccount.setUpdate_by(memberAccount.getUsername());

		Integer id = memberAccountMapper.insert(memberAccount);
		System.out.println(id);
	}
	
	@Test
	public void findMemberAccountByUsername() {
		String username = "[email protected]";
		MemberAccount memberAccount = memberAccountMapper.findMemberAccountByUsername(username);
		if(memberAccount != null) System.out.println(memberAccount.toString());		
	}
	
	@Test
	public void update() {
		MemberAccount memberAccount = new MemberAccount();
		memberAccount.setId("1");
		memberAccount.setPassword("123456");
		memberAccount.setUpdate_by("[email protected]");

		Integer result = memberAccountMapper.update(memberAccount);
		System.out.println(result);		
	}
	
}

实作XML 方式

指定XML 档案位置

mybatis.mapper-locations=classpath:mappers/*.xml

建立持久层接口

package com.example.demo.mapper;

import com.example.demo.entity.MemberAccount;

public interface MemberAccountMapper {

	public Integer insert(MemberAccount memberAccount);
	public MemberAccount findMemberAccountByUsername(String username);
	public Integer update(MemberAccount memberAccount);
	
}

设定扫描持久层(Mapper)的路径

在上一个步骤定义持久层接口时并没有定义@Service 或是@Repository 之类的注解,为了让持久层能够被Spring 管理,推荐使用@MapperScan(...) 指定扫描路径,虽然也可以在Mapper 上使用@Mapper 注解,但指定扫描路径就不用每个Mapper 都下注解。

package com.example.demo;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan("com.example.demo.mapper")
public class SpringBootDemoApplication {

	public static void main(String[] args) {
		SpringApplication.run(SpringBootDemoApplication.class, args);
	}

}

建立XML 档案

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace 对应持久层的介面 -->
<mapper namespace="com.example.demo.mapper.MemberAccountMapper" >        <!-- id 对应持久层介面的方法,parameterType 为引数资料型别,resultType 为返回资料型别 -->
    <insert id="insert" parameterType="com.example.demo.entity.MemberAccount">
       INSERT INTO test_project.member_account (
          USERNAME, PASSWORD, SALT, 
       	  CREATE_BY, CREATE_TIME, UPDATE_BY, UPDATE_TIME
       )     
       VALUES (
          #{username}, #{password}, #{salt},
          #{create_by}, NOW(), #{update_by}, NOW()
       )
    </insert>

    <select id="findMemberAccountByUsername" parameterType="string" resultType="com.example.demo.entity.MemberAccount">
    	SELECT 
		    ID, USERNAME, PASSWORD, SALT
	    FROM 
       	   test_project.member_account
        WHERE 
       	   USERNAME = #{username}
    </select>

    <update id="update" parameterType="com.example.demo.entity.MemberAccount">
		UPDATE 
		   test_project.member_account 
		SET 
		   PASSWORD = #{password}, UPDATE_BY = #{update_by}, UPDATE_TIME = NOW()
		WHERE 
		   ID = #{id}
    </update>
</mapper>

Github

使用MyBatis 注解方式建立持久层及其测试方法
使用MyBatis XML方式建立持久层及其测试方法

参考网站

ORM介绍及ORM优点、缺点
SpringBoot - 第十五章 | MyBatis整合 (注解方式) | J.J.'s Blogs
SpringBoot - 第十六章 | MyBatis整合 (XML方式) | J.J.'s Blogs
SpringBoot + MyBatis(注解版),常用的SQL方法


<<:  AI ninja project [day 27] QLattice --进阶分类

>>:  Day12 SwiftUI 05 - Life Cycle - SwiftUI App

工程师要的是什麽?

「钱多事少离家近、睡觉睡到自然醒、位高权重责任轻;老板说话不用听、五年就领退休金、领钱领到手抽筋。」...

Day 29 - Android Studio 这几天以来的统整

Day 29 - Android Studio 这几天以来的统整 离我们铁人完赛只剩一天了,我今天就...

[访谈] APCS x 竞程选手 Colten

今天邀请到来自新化高中的 Colten 来分享他在高中竞程这条路上的种种历程。本次访谈有公开浏览,欢...

Day-15 : image_tag 咩啊抓用置入图片?

最近刚好在开发 遇到放置图片和logo的问题 所以特别上来写一篇文章 纪录自己最近学习到的新东西 i...

Day_24 Nginx/FRP/ZeroTie

前两天介绍两个以VPN以点对点加密方式连回我们所架设的网路。但这里有个问题是我们得具有硬体与实体IP...