国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【MyBatis-Plus】DML編程控制 代碼生成器(文末贈書)

這篇具有很好參考價(jià)值的文章主要介紹了【MyBatis-Plus】DML編程控制 代碼生成器(文末贈書)。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

1,DML編程控制

查詢相關(guān)的操作我們已經(jīng)介紹完了,緊接著我們需要對另外三個(gè),增刪改進(jìn)行內(nèi)容的講解。挨個(gè)來說明下,首先是新增(insert)中的內(nèi)容。

1. id生成策略控制

前面我們在新增的時(shí)候留了一個(gè)問題,就是新增成功后,主鍵ID是一個(gè)很長串的內(nèi)容,我們更想要的是按照數(shù)據(jù)庫表字段進(jìn)行自增長,在解決這個(gè)問題之前,我們先來分析下ID該如何選擇:

  • 不同的表應(yīng)用不同的id生成策略
    • 日志:自增(1,2,3,4,……)
    • 購物訂單:特殊規(guī)則(FQ23948AK3843)
    • 外賣單:關(guān)聯(lián)地區(qū)日期等信息(10 04 20200314 34 91)
    • 關(guān)系表:可省略id
    • ……

不同的業(yè)務(wù)采用的ID生成方式應(yīng)該是不一樣的,那么在MP中都提供了哪些主鍵生成策略,以及我們該如何進(jìn)行選擇?

在這里我們又需要用到MP的一個(gè)注解叫@TableId

知識點(diǎn)1:@TableId
名稱 @TableId
類型 屬性注解
位置 模型類中用于表示主鍵的屬性定義上方
作用 設(shè)置當(dāng)前類中主鍵屬性的生成策略
相關(guān)屬性 value(默認(rèn)):設(shè)置數(shù)據(jù)庫表主鍵名稱
type:設(shè)置主鍵屬性的生成策略,值查照IdType的枚舉值
1. 環(huán)境構(gòu)建

在構(gòu)建條件查詢之前,我們先來準(zhǔn)備下環(huán)境

  • 創(chuàng)建一個(gè)SpringBoot項(xiàng)目

  • pom.xml中添加對應(yīng)的依賴

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.5.0</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>com.itheima</groupId>
        <artifactId>mybatisplus_03_dml</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <properties>
            <java.version>1.8</java.version>
        </properties>
        <dependencies>
    
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-boot-starter</artifactId>
                <version>3.4.1</version>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter</artifactId>
            </dependency>
    
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
                <version>1.1.16</version>
            </dependency>
    
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <scope>runtime</scope>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
    
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.18.12</version>
            </dependency>
    
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    
    </project>
    
    
  • 編寫UserDao接口

    @Mapper
    public interface UserDao extends BaseMapper<User> {
    }
    
  • 編寫模型類

    @Data
    
    public class User {
        private Long id;
        private String name;
        @TableField(value="pwd",select=false)
        private String password;
        private Integer age;
        private String tel;
        @TableField(exist=false)
        private Integer online;
    }
    
  • 編寫引導(dǎo)類

    @SpringBootApplication
    public class Mybatisplus03DqlApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(Mybatisplus03DqlApplication.class, args);
        }
    
    }
    
  • 編寫配置文件

    # dataSource
    spring:
      datasource:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/mybatisplus_db?serverTimezone=UTC
        username: root
        password: root
    # mp日志
    mybatis-plus:
      configuration:
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    
  • 編寫測試類

    @SpringBootTest
    class Mybatisplus02DqlApplicationTests {
    
        @Autowired
        private UserDao userDao;
        
        @Test
        void testGetAll(){
            List<User> userList = userDao.selectList(null);
            System.out.println(userList);
        }
    }
    
  • 測試

    @SpringBootTest
    class Mybatisplus03DqlApplicationTests {
    
        @Autowired
        private UserDao userDao;
    	
    
    
    
    }
    
  • 最終創(chuàng)建的項(xiàng)目結(jié)構(gòu)為:

【MyBatis-Plus】DML編程控制 代碼生成器(文末贈書),SSM,mybatis,java,spring,spring boot,mysql

2. 代碼演示
AUTO策略
步驟1:設(shè)置生成策略為AUTO
@Data

public class User {
    @TableId(type = IdType.AUTO)
    private Long id;
    private String name;
    @TableField(value="pwd",select=false)
    private String password;
    private Integer age;
    private String tel;
    @TableField(exist=false)
    private Integer online;
}
步驟2:刪除測試數(shù)據(jù)并修改自增值
  • 刪除測試數(shù)據(jù),讓當(dāng)前id呈現(xiàn)遞增

【MyBatis-Plus】DML編程控制 代碼生成器(文末贈書),SSM,mybatis,java,spring,spring boot,mysql

  • 因?yàn)橹吧芍麈IID的值比較長,會把MySQL的自動增長的值變的很大,所以需要將其調(diào)整為目前最新的id值。

【MyBatis-Plus】DML編程控制 代碼生成器(文末贈書),SSM,mybatis,java,spring,spring boot,mysql
【MyBatis-Plus】DML編程控制 代碼生成器(文末贈書),SSM,mybatis,java,spring,spring boot,mysql

步驟3:運(yùn)行新增方法
    @Test
    void insert() {
        User user = new User();
        user.setName("熱愛編程的小白白");
        user.setPassword("root");
        user.setAge(22);
        user.setTel("1768543416");
        userDao.insert(user);

    }

會發(fā)現(xiàn),新增成功,并且主鍵id也是從5開始

【MyBatis-Plus】DML編程控制 代碼生成器(文末贈書),SSM,mybatis,java,spring,spring boot,mysql

經(jīng)過這三步的演示,會發(fā)現(xiàn)AUTO的作用是使用數(shù)據(jù)庫ID自增,在使用該策略的時(shí)候一定要確保對應(yīng)的數(shù)據(jù)庫表設(shè)置了ID主鍵自增,否則無效。

接下來,我們可以進(jìn)入源碼查看下ID的生成策略有哪些?

打開源碼后,你會發(fā)現(xiàn)并沒有看到中文注釋,這就需要我們點(diǎn)擊右上角的Download Sources,會自動幫你把這個(gè)類的java文件下載下來,我們就能看到具體的注釋內(nèi)容。因?yàn)檫@個(gè)技術(shù)是國人制作的,所以他代碼中的注釋還是比較容易看懂的。

【MyBatis-Plus】DML編程控制 代碼生成器(文末贈書),SSM,mybatis,java,spring,spring boot,mysql

當(dāng)把源碼下載完后,就可以看到如下內(nèi)容:

【MyBatis-Plus】DML編程控制 代碼生成器(文末贈書),SSM,mybatis,java,spring,spring boot,mysql

從源碼中可以看到,除了AUTO這個(gè)策略以外,還有如下幾種生成策略:

  • NONE: 不設(shè)置id生成策略
  • INPUT:用戶手工輸入id
  • ASSIGN_ID:雪花算法生成id(可兼容數(shù)值型與字符串型)
  • ASSIGN_UUID:以UUID生成算法作為id生成策略
  • 其他的幾個(gè)策略均已過時(shí),都將被ASSIGN_ID和ASSIGN_UUID代替掉。

拓展:

分布式ID是什么?

  • 當(dāng)數(shù)據(jù)量足夠大的時(shí)候,一臺數(shù)據(jù)庫服務(wù)器存儲不下,這個(gè)時(shí)候就需要多臺數(shù)據(jù)庫服務(wù)器進(jìn)行存儲
  • 比如訂單表就有可能被存儲在不同的服務(wù)器上
  • 如果用數(shù)據(jù)庫表的自增主鍵,因?yàn)樵趦膳_服務(wù)器上所以會出現(xiàn)沖突
  • 這個(gè)時(shí)候就需要一個(gè)全局唯一ID,這個(gè)ID就是分布式ID。
INPUT策略
步驟1:設(shè)置生成策略為INPUT
@Data

public class User {
    @TableId(type = IdType.INPUT)
    private Long id;
    private String name;
    @TableField(value="pwd",select=false)
    private String password;
    private Integer age;
    private String tel;
    @TableField(exist=false)
    private Integer online;
}

注意: 這種ID生成策略,需要將表的自增策略刪除掉

【MyBatis-Plus】DML編程控制 代碼生成器(文末贈書),SSM,mybatis,java,spring,spring boot,mysql

步驟2:添加數(shù)據(jù)手動設(shè)置ID
@SpringBootTest
class Mybatisplus03DqlApplicationTests {

    @Autowired
    private UserDao userDao;
	
    @Test
    void testSave(){
        User user = new User();
        //設(shè)置主鍵ID的值
        user.setId(666L);
        user.setName("加油!");
        user.setPassword("沖沖沖");
        user.setAge(12);
        user.setTel("18754434333");
        userDao.insert(user);
    }
}
步驟3:運(yùn)行新增方法

如果沒有設(shè)置主鍵ID的值,則會報(bào)錯,錯誤提示就是主鍵ID沒有給值:

【MyBatis-Plus】DML編程控制 代碼生成器(文末贈書),SSM,mybatis,java,spring,spring boot,mysql

如果設(shè)置了主鍵ID,則數(shù)據(jù)添加成功,如下:

【MyBatis-Plus】DML編程控制 代碼生成器(文末贈書),SSM,mybatis,java,spring,spring boot,mysql

ASSIGN_ID策略
步驟1:設(shè)置生成策略為ASSIGN_ID
@Data

public class User {
    @TableId(type = IdType.ASSIGN_ID)
    private Long id;
    private String name;
    @TableField(value="pwd",select=false)
    private String password;
    private Integer age;
    private String tel;
    @TableField(exist=false)
    private Integer online;
}
步驟2:添加數(shù)據(jù)不設(shè)置ID
@SpringBootTest
class Mybatisplus03DqlApplicationTests {

    @Autowired
    private UserDao userDao;
	
    @Test
    void testSave(){
        User user = new User();
        user.setName("張三");
        user.setPassword("123466");
        user.setAge(32);
        user.setTel("125607778989");
        userDao.insert(user);
    }
}

注意: 這種生成策略,不需要手動設(shè)置ID,如果手動設(shè)置ID,則會使用自己設(shè)置的值。

步驟3:運(yùn)行新增方法

【MyBatis-Plus】DML編程控制 代碼生成器(文末贈書),SSM,mybatis,java,spring,spring boot,mysql

生成的ID就是一個(gè)Long類型的數(shù)據(jù)。

ASSIGN_UUID策略
步驟1:設(shè)置生成策略為ASSIGN_UUID

使用uuid需要注意的是,主鍵的類型不能是Long,而應(yīng)該改成String類型

@Data

public class User {
    @TableId(type = IdType.ASSIGN_UUID)
    private String id;
    private String name;
    @TableField(value="pwd",select=false)
    private String password;
    private Integer age;
    private String tel;
    @TableField(exist=false)
    private Integer online;
}
步驟2:修改表的主鍵類型

【MyBatis-Plus】DML編程控制 代碼生成器(文末贈書),SSM,mybatis,java,spring,spring boot,mysql

主鍵類型設(shè)置為varchar,長度要大于32,因?yàn)閁UID生成的主鍵為32位,如果長度小的話就會導(dǎo)致插入失敗。

步驟3:添加數(shù)據(jù)不設(shè)置ID
@SpringBootTest
class Mybatisplus03DqlApplicationTests {

    @Autowired
    private UserDao userDao;
	
    @Test
    void insert() {
        User user = new User();
        user.setName("張三");
        user.setPassword("123466");
        user.setAge(32);
        user.setTel("125607778989");
        userDao.insert(user);

    }
}
步驟4:運(yùn)行新增方法

【MyBatis-Plus】DML編程控制 代碼生成器(文末贈書),SSM,mybatis,java,spring,spring boot,mysql

【MyBatis-Plus】DML編程控制 代碼生成器(文末贈書),SSM,mybatis,java,spring,spring boot,mysql

接下來我們來聊一聊雪花算法:

雪花算法(SnowFlake),是Twitter官方給出的算法實(shí)現(xiàn) 是用Scala寫的。其生成的結(jié)果是一個(gè)64bit大小整數(shù),它的結(jié)構(gòu)如下圖:

【MyBatis-Plus】DML編程控制 代碼生成器(文末贈書),SSM,mybatis,java,spring,spring boot,mysql

  1. 1bit,不用,因?yàn)槎M(jìn)制中最高位是符號位,1表示負(fù)數(shù),0表示正數(shù)。生成的id一般都是用整數(shù),所以最高位固定為0。
  2. 41bit-時(shí)間戳,用來記錄時(shí)間戳,毫秒級
  3. 10bit-工作機(jī)器id,用來記錄工作機(jī)器id,其中高位5bit是數(shù)據(jù)中心ID其取值范圍0-31,低位5bit是工作節(jié)點(diǎn)ID其取值范圍0-31,兩個(gè)組合起來最多可以容納1024個(gè)節(jié)點(diǎn)
  4. 序列號占用12bit,每個(gè)節(jié)點(diǎn)每毫秒0開始不斷累加,最多可以累加到4095,一共可以產(chǎn)生4096個(gè)ID
3. ID生成策略對比

介紹了這些主鍵ID的生成策略,我們以后該用哪個(gè)呢?

  • NONE: 不設(shè)置id生成策略,MP不自動生成,約等于INPUT,所以這兩種方式都需要用戶手動設(shè)置,但是手動設(shè)置第一個(gè)問題是容易出現(xiàn)相同的ID造成主鍵沖突,為了保證主鍵不沖突就需要做很多判定,實(shí)現(xiàn)起來比較復(fù)雜
  • AUTO:數(shù)據(jù)庫ID自增,這種策略適合在數(shù)據(jù)庫服務(wù)器只有1臺的情況下使用,不可作為分布式ID使用
  • ASSIGN_UUID:可以在分布式的情況下使用,而且能夠保證唯一,但是生成的主鍵是32位的字符串,長度過長占用空間而且還不能排序,查詢性能也慢
  • ASSIGN_ID:可以在分布式的情況下使用,生成的是Long類型的數(shù)字,可以排序性能也高,但是生成的策略和服務(wù)器時(shí)間有關(guān),如果修改了系統(tǒng)時(shí)間就有可能導(dǎo)致出現(xiàn)重復(fù)主鍵
  • 綜上所述,每一種主鍵策略都有自己的優(yōu)缺點(diǎn),根據(jù)自己項(xiàng)目業(yè)務(wù)的實(shí)際情況來選擇使用才是最明智的選擇。
4. 簡化配置

前面我們已經(jīng)完成了表關(guān)系映射、數(shù)據(jù)庫主鍵策略的設(shè)置,接下來對于這兩個(gè)內(nèi)容的使用,我們再講下他們的簡化配置:

模型類主鍵策略設(shè)置

【MyBatis-Plus】DML編程控制 代碼生成器(文末贈書),SSM,mybatis,java,spring,spring boot,mysql

確實(shí)是稍微有點(diǎn)繁瑣,我們能不能在某一處進(jìn)行配置,就能讓所有的模型類都可以使用該主鍵ID策略呢?

答案是肯定有,我們只需要在配置文件中添加如下內(nèi)容:

mybatis-plus:
  global-config:
    db-config:
    	id-type: assign_id

配置完成后,每個(gè)模型類的主鍵ID策略都將成為assign_id.

數(shù)據(jù)庫表與模型類的映射關(guān)系

MP會默認(rèn)將模型類的類名名首字母小寫作為表名使用,假如數(shù)據(jù)庫表的名稱都以tbl_開頭,那么我們就需要將所有的模型類上添加@TableName,如:

【MyBatis-Plus】DML編程控制 代碼生成器(文末贈書),SSM,mybatis,java,spring,spring boot,mysql

配置起來還是比較繁瑣,簡化方式為在配置文件中配置如下內(nèi)容:

mybatis-plus:
  global-config:
    db-config:
    	table-prefix: tbl_

設(shè)置表的前綴內(nèi)容,這樣MP就會拿 tbl_加上模型類的首字母小寫,就剛好組裝成數(shù)據(jù)庫的表名。

2. 多記錄操作

先來看下問題:

【MyBatis-Plus】DML編程控制 代碼生成器(文末贈書),SSM,mybatis,java,spring,spring boot,mysql

之前添加了很多商品到購物車,過了幾天發(fā)現(xiàn)這些東西又不想要了,該怎么辦呢?

很簡單刪除掉,但是一個(gè)個(gè)刪除的話還是比較慢和費(fèi)事的,所以一般會給用戶一個(gè)批量操作,也就是前面有一個(gè)復(fù)選框,用戶一次可以勾選多個(gè)也可以進(jìn)行全選,然后刪一次就可以將購物車清空,這個(gè)就需要用到批量刪除的操作了。

具體該如何實(shí)現(xiàn)多條刪除,我們找找對應(yīng)的API方法

int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);

翻譯方法的字面意思為:刪除(根據(jù)ID 批量刪除),參數(shù)是一個(gè)集合,可以存放多個(gè)id值。

需求:根據(jù)傳入的id集合將數(shù)據(jù)庫表中的數(shù)據(jù)刪除掉。

@SpringBootTest
class Mybatisplus03DqlApplicationTests {

    @Autowired
    private UserDao userDao;
	
    @Test
    void testDelete(){
        //刪除指定多條數(shù)據(jù)
        List<Long> list = new ArrayList<>();
        list.add(1402551342481838081L);
        list.add(1402553134049501186L);
        list.add(1402553619611430913L);
        userDao.deleteBatchIds(list);
    }
}

執(zhí)行成功后,數(shù)據(jù)庫表中的數(shù)據(jù)就會按照指定的id進(jìn)行刪除。

除了按照id集合進(jìn)行批量刪除,也可以按照id集合進(jìn)行批量查詢,還是先來看下API

List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);

方法名稱翻譯為:查詢(根據(jù)ID 批量查詢),參數(shù)是一個(gè)集合,可以存放多個(gè)id值。

需求:根據(jù)傳入的ID集合查詢用戶信息

@SpringBootTest
class Mybatisplus03DqlApplicationTests {

    @Autowired
    private UserDao userDao;
	
    @Test
    void testGetByIds(){
        //查詢指定多條數(shù)據(jù)
        List<Long> list = new ArrayList<>();
        list.add(1L);
        list.add(3L);
        list.add(4L);
        userDao.selectBatchIds(list);
    }
}

查詢結(jié)果就會按照指定傳入的id值進(jìn)行查詢

【MyBatis-Plus】DML編程控制 代碼生成器(文末贈書),SSM,mybatis,java,spring,spring boot,mysql

3. 邏輯刪除

接下來要講解是刪除中比較重要的一個(gè)操作,邏輯刪除,先來分析下問題:

【MyBatis-Plus】DML編程控制 代碼生成器(文末贈書),SSM,mybatis,java,spring,spring boot,mysql

  • 這是一個(gè)員工和其所簽的合同表,關(guān)系是一個(gè)員工可以簽多個(gè)合同,是一個(gè)一(員工)對多(合同)的表

  • 員工ID為1的張業(yè)績,總共簽了三個(gè)合同,如果此時(shí)他離職了,我們需要將員工表中的數(shù)據(jù)進(jìn)行刪除,會執(zhí)行delete操作

  • 如果表在設(shè)計(jì)的時(shí)候有主外鍵關(guān)系,那么同時(shí)也得將合同表中的前三條數(shù)據(jù)也刪除掉

【MyBatis-Plus】DML編程控制 代碼生成器(文末贈書),SSM,mybatis,java,spring,spring boot,mysql

  • 后期要統(tǒng)計(jì)所簽合同的總金額,就會發(fā)現(xiàn)對不上,原因是已經(jīng)將員工1簽的合同信息刪除掉了

  • 如果只刪除員工不刪除合同表數(shù)據(jù),那么合同的員工編號對應(yīng)的員工信息不存在,那么就會出現(xiàn)垃圾數(shù)據(jù),就會出現(xiàn)無主合同,根本不知道有張業(yè)績這個(gè)人的存在

  • 所以經(jīng)過分析,我們不應(yīng)該將表中的數(shù)據(jù)刪除掉,而是需要進(jìn)行保留,但是又得把離職的人和在職的人進(jìn)行區(qū)分,這樣就解決了上述問題,如:

【MyBatis-Plus】DML編程控制 代碼生成器(文末贈書),SSM,mybatis,java,spring,spring boot,mysql

  • 區(qū)分的方式,就是在員工表中添加一列數(shù)據(jù)deleted,如果為0說明在職員工,如果離職則將其改完1,(0和1所代表的含義是可以自定義的)

所以對于刪除操作業(yè)務(wù)問題來說有:

  • 物理刪除:業(yè)務(wù)數(shù)據(jù)從數(shù)據(jù)庫中丟棄,執(zhí)行的是delete操作
  • 邏輯刪除:為數(shù)據(jù)設(shè)置是否可用狀態(tài)字段,刪除時(shí)設(shè)置狀態(tài)字段為不可用狀態(tài),數(shù)據(jù)保留在數(shù)據(jù)庫中,執(zhí)行的是update操作

MP中邏輯刪除具體該如何實(shí)現(xiàn)?

步驟1:修改數(shù)據(jù)庫表添加deleted

字段名可以任意,內(nèi)容也可以自定義,比如0代表正常,1代表刪除,可以在添加列的同時(shí)設(shè)置其默認(rèn)值為0正常。

【MyBatis-Plus】DML編程控制 代碼生成器(文末贈書),SSM,mybatis,java,spring,spring boot,mysql
【MyBatis-Plus】DML編程控制 代碼生成器(文末贈書),SSM,mybatis,java,spring,spring boot,mysql

步驟2:實(shí)體類添加屬性

(1)添加與數(shù)據(jù)庫表的列對應(yīng)的一個(gè)屬性名,名稱可以任意,如果和數(shù)據(jù)表列名對不上,可以使用@TableField進(jìn)行關(guān)系映射,如果一致,則會自動對應(yīng)。

(2)標(biāo)識新增的字段為邏輯刪除字段,使用@TableLogic

@Data
//@TableName("tbl_user") 可以不寫是因?yàn)榕渲昧巳峙渲?/span>
public class User {
    @TableId(type = IdType.ASSIGN_UUID)
    private String id;
    private String name;
    @TableField(value="pwd",select=false)
    private String password;
    private Integer age;
    private String tel;
    @TableField(exist=false)
    private Integer online;
    @TableLogic(value="0",delval="1")
    //value為正常數(shù)據(jù)的值,delval為刪除數(shù)據(jù)的值
    private Integer deleted;
}
步驟3:運(yùn)行刪除方法
@SpringBootTest
class Mybatisplus03DqlApplicationTests {

    @Autowired
    private UserDao userDao;
	
    @Test
    void testDelete(){
       userDao.deleteById(1L);
    }
}

【MyBatis-Plus】DML編程控制 代碼生成器(文末贈書),SSM,mybatis,java,spring,spring boot,mysql

從測試結(jié)果來看,邏輯刪除最后走的是update操作,會將指定的字段修改成刪除狀態(tài)對應(yīng)的值。

思考

邏輯刪除,對查詢有沒有影響呢?

  • 執(zhí)行查詢操作

    @SpringBootTest
    class Mybatisplus03DqlApplicationTests {
    
        @Autowired
        private UserDao userDao;
    	
        @Test
        void testFind(){
           System.out.println(userDao.selectList(null));
        }
    }
    

    運(yùn)行測試,會發(fā)現(xiàn)打印出來的sql語句中會多一個(gè)查詢條件,如:

【MyBatis-Plus】DML編程控制 代碼生成器(文末贈書),SSM,mybatis,java,spring,spring boot,mysql

可想而知,MP的邏輯刪除會將所有的查詢都添加一個(gè)未被刪除的條件,也就是已經(jīng)被刪除的數(shù)據(jù)是不應(yīng)該被查詢出來的。

  • 如果還是想把已經(jīng)刪除的數(shù)據(jù)都查詢出來該如何實(shí)現(xiàn)呢?

    @Mapper
    public interface UserDao extends BaseMapper<User> {
        //查詢所有數(shù)據(jù)包含已經(jīng)被刪除的數(shù)據(jù)
        @Select("select * from tbl_user")
        public List<User> selectAll();
    }
    
  • 如果每個(gè)表都要有邏輯刪除,那么就需要在每個(gè)模型類的屬性上添加@TableLogic注解,如何優(yōu)化?

    在配置文件中添加全局配置,如下:

    mybatis-plus:
      global-config:
        db-config:
          # 邏輯刪除字段名
          logic-delete-field: deleted
          # 邏輯刪除字面值:未刪除為0
          logic-not-delete-value: 0
          # 邏輯刪除字面值:刪除為1
          logic-delete-value: 1
    

介紹完邏輯刪除,邏輯刪除的本質(zhì)為:

邏輯刪除的本質(zhì)其實(shí)是修改操作。如果加了邏輯刪除字段,查詢數(shù)據(jù)時(shí)也會自動帶上邏輯刪除字段。

執(zhí)行的SQL語句為:

UPDATE tbl_user SET deleted=1 where id = ? AND deleted=0

執(zhí)行數(shù)據(jù)結(jié)果為:

【MyBatis-Plus】DML編程控制 代碼生成器(文末贈書),SSM,mybatis,java,spring,spring boot,mysql

知識點(diǎn)1:@TableLogic
名稱 @TableLogic
類型 屬性注解
位置 模型類中用于表示刪除字段的屬性定義上方
作用 標(biāo)識該字段為進(jìn)行邏輯刪除的字段
相關(guān)屬性 value:邏輯未刪除值
delval:邏輯刪除值

4. 樂觀鎖

1. 概念

在講解樂觀鎖之前,我們還是先來分析下問題:

業(yè)務(wù)并發(fā)現(xiàn)象帶來的問題:秒殺

  • 假如有100個(gè)商品或者票在出售,為了能保證每個(gè)商品或者票只能被一個(gè)人購買,如何保證不會出現(xiàn)超買或者重復(fù)賣
  • 對于這一類問題,其實(shí)有很多的解決方案可以使用
  • 第一個(gè)最先想到的就是鎖,鎖在一臺服務(wù)器中是可以解決的,但是如果在多臺服務(wù)器下鎖就沒有辦法控制,比如12306有兩臺服務(wù)器在進(jìn)行賣票,在兩臺服務(wù)器上都添加鎖的話,那也有可能會導(dǎo)致在同一時(shí)刻有兩個(gè)線程在進(jìn)行賣票,還是會出現(xiàn)并發(fā)問題
  • 我們接下來介紹的這種方式是針對于小型企業(yè)的解決方案,因?yàn)閿?shù)據(jù)庫本身的性能就是個(gè)瓶頸,如果對其并發(fā)量超過2000以上的就需要考慮其他的解決方案了。

簡單來說,樂觀鎖主要解決的問題是當(dāng)要更新一條記錄的時(shí)候,希望這條記錄沒有被別人更新。

2. 實(shí)現(xiàn)思路

樂觀鎖的實(shí)現(xiàn)方式:

  • 數(shù)據(jù)庫表中添加version列,比如默認(rèn)值給1
  • 第一個(gè)線程要修改數(shù)據(jù)之前,取出記錄時(shí),獲取當(dāng)前數(shù)據(jù)庫中的version=1
  • 第二個(gè)線程要修改數(shù)據(jù)之前,取出記錄時(shí),獲取當(dāng)前數(shù)據(jù)庫中的version=1
  • 第一個(gè)線程執(zhí)行更新時(shí),set version = newVersion where version = oldVersion
    • newVersion = version+1 [2]
    • oldVersion = version [1]
  • 第二個(gè)線程執(zhí)行更新時(shí),set version = newVersion where version = oldVersion
    • newVersion = version+1 [2]
    • oldVersion = version [1]
  • 假如這兩個(gè)線程都來更新數(shù)據(jù),第一個(gè)和第二個(gè)線程都可能先執(zhí)行
    • 假如第一個(gè)線程先執(zhí)行更新,會把version改為2,
    • 第二個(gè)線程再更新的時(shí)候,set version = 2 where version = 1,此時(shí)數(shù)據(jù)庫表的數(shù)據(jù)version已經(jīng)為2,所以第二個(gè)線程會修改失敗
    • 假如第二個(gè)線程先執(zhí)行更新,會把version改為2,
    • 第一個(gè)線程再更新的時(shí)候,set version = 2 where version = 1,此時(shí)數(shù)據(jù)庫表的數(shù)據(jù)version已經(jīng)為2,所以第一個(gè)線程會修改失敗
    • 不管誰先執(zhí)行都會確保只能有一個(gè)線程更新數(shù)據(jù),這就是MP提供的樂觀鎖的實(shí)現(xiàn)原理分析。

上面所說的步驟具體該如何實(shí)現(xiàn)呢?

3. 實(shí)現(xiàn)步驟

分析完步驟后,具體的實(shí)現(xiàn)步驟如下:

步驟1:數(shù)據(jù)庫表添加列

列名可以任意,比如使用version,給列設(shè)置默認(rèn)值為1

【MyBatis-Plus】DML編程控制 代碼生成器(文末贈書),SSM,mybatis,java,spring,spring boot,mysql

步驟2:在模型類中添加對應(yīng)的屬性

根據(jù)添加的字段列名,在模型類中添加對應(yīng)的屬性值

@Data
//@TableName("tbl_user") 可以不寫是因?yàn)榕渲昧巳峙渲?/span>
public class User {
    @TableId(type = IdType.ASSIGN_UUID)
    private String id;
    private String name;
    @TableField(value="pwd",select=false)
    private String password;
    private Integer age;
    private String tel;
    @TableField(exist=false)
    private Integer online;
    private Integer deleted;
    @Version
    private Integer version;
}
步驟3:添加樂觀鎖的攔截器
@Configuration
public class MpConfig {
    @Bean
    public MybatisPlusInterceptor mpInterceptor() {
        //1.定義Mp攔截器
        MybatisPlusInterceptor mpInterceptor = new MybatisPlusInterceptor();
        //2.添加樂觀鎖攔截器
        mpInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return mpInterceptor;
    }
}
步驟4:執(zhí)行更新操作
@SpringBootTest
class Mybatisplus03DqlApplicationTests {

    @Autowired
    private UserDao userDao;
	
    @Test
    void testUpdate(){
       User user = new User();
        user.setId(3L);
        user.setName("Jock666");
        userDao.updateById(user);
    }
}

【MyBatis-Plus】DML編程控制 代碼生成器(文末贈書),SSM,mybatis,java,spring,spring boot,mysql

你會發(fā)現(xiàn),這次修改并沒有更新version字段,原因是沒有攜帶version數(shù)據(jù)。

添加version數(shù)據(jù)

@SpringBootTest
class Mybatisplus03DqlApplicationTests {

    @Autowired
    private UserDao userDao;
	
    @Test
    void testUpdate(){
        User user = new User();
        user.setId("3");
        user.setName("Jock666");
        user.setVersion(1);
        userDao.updateById(user);
    }
}

【MyBatis-Plus】DML編程控制 代碼生成器(文末贈書),SSM,mybatis,java,spring,spring boot,mysql

你會發(fā)現(xiàn),我們傳遞的是1,MP會將1進(jìn)行加1,然后,更新回到數(shù)據(jù)庫表中。

【MyBatis-Plus】DML編程控制 代碼生成器(文末贈書),SSM,mybatis,java,spring,spring boot,mysql

所以要想實(shí)現(xiàn)樂觀鎖,首先第一步應(yīng)該是拿到表中的version,然后拿version當(dāng)條件在將version加1更新回到數(shù)據(jù)庫表中,所以我們在查詢的時(shí)候,需要對其進(jìn)行查詢

@SpringBootTest
class Mybatisplus03DqlApplicationTests {

    @Autowired
    private UserDao userDao;
	
    @Test
    void testUpdate(){
        //1.先通過要修改的數(shù)據(jù)id將當(dāng)前數(shù)據(jù)查詢出來
        User user = userDao.selectById(3L);
        //2.將要修改的屬性逐一設(shè)置進(jìn)去
        user.setName("Jock888");
        userDao.updateById(user);
    }
}

【MyBatis-Plus】DML編程控制 代碼生成器(文末贈書),SSM,mybatis,java,spring,spring boot,mysql

大概分析完樂觀鎖的實(shí)現(xiàn)步驟以后,我們來模擬一種加鎖的情況,看看能不能實(shí)現(xiàn)多個(gè)人修改同一個(gè)數(shù)據(jù)的時(shí)候,只能有一個(gè)人修改成功。

@SpringBootTest
class Mybatisplus03DqlApplicationTests {

    @Autowired
    private UserDao userDao;
	
    @Test
    void testUpdate(){
       //1.先通過要修改的數(shù)據(jù)id將當(dāng)前數(shù)據(jù)查詢出來
        User user = userDao.selectById(3L);     //version=3
        User user2 = userDao.selectById(3L);    //version=3
        user2.setName("Jock aaa");
        userDao.updateById(user2);              //version=>4
        user.setName("Jock bbb");
        userDao.updateById(user);               //verion=3?條件還成立嗎?
    }
}

運(yùn)行程序,分析結(jié)果:

【MyBatis-Plus】DML編程控制 代碼生成器(文末贈書),SSM,mybatis,java,spring,spring boot,mysql
【MyBatis-Plus】DML編程控制 代碼生成器(文末贈書),SSM,mybatis,java,spring,spring boot,mysql

樂觀鎖就已經(jīng)實(shí)現(xiàn)完成了,如果對于上面的這些步驟記不住咋辦呢?

參考官方文檔來實(shí)現(xiàn):

https://mp.baomidou.com/guide/interceptor-optimistic-locker.html#optimisticlockerinnerinterceptor

【MyBatis-Plus】DML編程控制 代碼生成器(文末贈書),SSM,mybatis,java,spring,spring boot,mysql

2,快速開發(fā)

1. 代碼生成器原理分析

造句:
【MyBatis-Plus】DML編程控制 代碼生成器(文末贈書),SSM,mybatis,java,spring,spring boot,mysql

我們可以往空白內(nèi)容進(jìn)行填詞造句,比如:
【MyBatis-Plus】DML編程控制 代碼生成器(文末贈書),SSM,mybatis,java,spring,spring boot,mysql

再比如:
【MyBatis-Plus】DML編程控制 代碼生成器(文末贈書),SSM,mybatis,java,spring,spring boot,mysql

觀察我們之前寫的代碼,會發(fā)現(xiàn)其中也會有很多重復(fù)內(nèi)容,比如:

【MyBatis-Plus】DML編程控制 代碼生成器(文末贈書),SSM,mybatis,java,spring,spring boot,mysql

那我們就想,如果我想做一個(gè)Book模塊的開發(fā),是不是只需要將紅色部分的內(nèi)容全部更換成Book即可,如:

【MyBatis-Plus】DML編程控制 代碼生成器(文末贈書),SSM,mybatis,java,spring,spring boot,mysql

所以我們會發(fā)現(xiàn),做任何模塊的開發(fā),對于這段代碼,基本上都是對紅色部分的調(diào)整,所以我們把去掉紅色內(nèi)容的東西稱之為模板,紅色部分稱之為參數(shù),以后只需要傳入不同的參數(shù),就可以根據(jù)模板創(chuàng)建出不同模塊的dao代碼。

除了Dao可以抽取模塊,其實(shí)我們常見的類都可以進(jìn)行抽取,只要他們有公共部分即可。再來看下模型類的模板:

【MyBatis-Plus】DML編程控制 代碼生成器(文末贈書),SSM,mybatis,java,spring,spring boot,mysql

  • ① 可以根據(jù)數(shù)據(jù)庫表的表名來填充
  • ② 可以根據(jù)用戶的配置來生成ID生成策略
  • ③到⑨可以根據(jù)數(shù)據(jù)庫表字段名稱來填充

所以只要我們知道是對哪張表進(jìn)行代碼生成,這些內(nèi)容我們都可以進(jìn)行填充。

分析完后,我們會發(fā)現(xiàn),要想完成代碼自動生成,我們需要有以下內(nèi)容:

  • 模板: MyBatisPlus提供,可以自己提供,但是麻煩,不建議
  • 數(shù)據(jù)庫相關(guān)配置:讀取數(shù)據(jù)庫獲取表和字段信息
  • 開發(fā)者自定義配置:手工配置,比如ID生成策略

2. 代碼生成器實(shí)現(xiàn)

步驟1:創(chuàng)建一個(gè)Maven項(xiàng)目
代碼2:導(dǎo)入對應(yīng)的jar包
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.1</version>
    </parent>
    <groupId>com.itheima</groupId>
    <artifactId>mybatisplus_04_generator</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <!--spring webmvc-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--mybatisplus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.1</version>
        </dependency>

        <!--druid-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.16</version>
        </dependency>

        <!--mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

        <!--test-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
        </dependency>

        <!--代碼生成器-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.4.1</version>
        </dependency>

        <!--velocity模板引擎-->
        <dependency>
            <groupId>org.apache.velocity</groupId>
            <artifactId>velocity-engine-core</artifactId>
            <version>2.3</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

步驟3:編寫引導(dǎo)類
package com.itheima;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;

public class CodeGenerator {
    public static void main(String[] args) {
        //1.獲取代碼生成器的對象
        AutoGenerator autoGenerator = new AutoGenerator();

        //設(shè)置數(shù)據(jù)庫相關(guān)配置
        DataSourceConfig dataSource = new DataSourceConfig();
        dataSource.setDriverName("com.mysql.cj.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/mybatisplus_db?serverTimezone=UTC");
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        autoGenerator.setDataSource(dataSource);

        //設(shè)置全局配置
        GlobalConfig globalConfig = new GlobalConfig();
        globalConfig.setOutputDir(System.getProperty("user.dir")+"/src/main/java");    //設(shè)置代碼生成位置
        globalConfig.setOpen(false);    //設(shè)置生成完畢后是否打開生成代碼所在的目錄
        globalConfig.setAuthor("熱愛編程的小白白");    //設(shè)置作者
        globalConfig.setFileOverride(true);     //設(shè)置是否覆蓋原始生成的文件
        globalConfig.setMapperName("%sDao");    //設(shè)置數(shù)據(jù)層接口名,%s為占位符,指代模塊名稱
        globalConfig.setIdType(IdType.AUTO);   //設(shè)置Id生成策略
        autoGenerator.setGlobalConfig(globalConfig);

        //設(shè)置包名相關(guān)配置
        PackageConfig packageInfo = new PackageConfig();
        packageInfo.setParent("com.aaa");   //設(shè)置生成的包名,與代碼所在位置不沖突,二者疊加組成完整路徑
        packageInfo.setEntity("domain");    //設(shè)置實(shí)體類包名
        packageInfo.setMapper("dao");   //設(shè)置數(shù)據(jù)層包名
        autoGenerator.setPackageInfo(packageInfo);

        //策略設(shè)置
        StrategyConfig strategyConfig = new StrategyConfig();
        strategyConfig.setInclude("user");  //設(shè)置當(dāng)前參與生成的表名,參數(shù)為可變參數(shù)
//        strategyConfig.setTablePrefix("tbl_");  //設(shè)置數(shù)據(jù)庫表的前綴名稱,模塊名 = 數(shù)據(jù)庫表名 - 前綴名  例如: User = tbl_user - tbl_
        strategyConfig.setRestControllerStyle(true);    //設(shè)置是否啟用Rest風(fēng)格
        strategyConfig.setVersionFieldName("version");  //設(shè)置樂觀鎖字段名
        strategyConfig.setLogicDeleteFieldName("deleted");  //設(shè)置邏輯刪除字段名
        strategyConfig.setEntityLombokModel(true);  //設(shè)置是否啟用lombok
        autoGenerator.setStrategy(strategyConfig);
        //2.執(zhí)行生成操作
        autoGenerator.execute();
    }
}

對于代碼生成器中的代碼內(nèi)容,我們可以直接從官方文檔中獲取代碼進(jìn)行修改,

https://mp.baomidou.com/guide/generator.html

步驟5:運(yùn)行程序

運(yùn)行成功后,會在當(dāng)前項(xiàng)目中生成很多代碼,代碼包含controller,service,mapperentity

【MyBatis-Plus】DML編程控制 代碼生成器(文末贈書),SSM,mybatis,java,spring,spring boot,mysql

至此代碼生成器就已經(jīng)完成工作,我們能快速根據(jù)數(shù)據(jù)庫表來創(chuàng)建對應(yīng)的類,簡化我們的代碼開發(fā)。

3. MP中Service的CRUD

回顧我們之前業(yè)務(wù)層代碼的編寫,編寫接口和對應(yīng)的實(shí)現(xiàn)類:

public interface UserService{
	
}

@Service
public class UserServiceImpl implements UserService{

}

接口和實(shí)現(xiàn)類有了以后,需要在接口和實(shí)現(xiàn)類中聲明方法

public interface UserService{
	public List<User> findAll();
}

@Service
public class UserServiceImpl implements UserService{
    @Autowired
    private UserDao userDao;
    
	public List<User> findAll(){
        return userDao.selectList(null);
    }
}

MP看到上面的代碼以后就說這些方法也是比較固定和通用的,那我來幫你抽取下,所以MP提供了一個(gè)Service接口和實(shí)現(xiàn)類,分別是:IServiceServiceImpl,后者是對前者的一個(gè)具體實(shí)現(xiàn)。

以后我們自己寫的Service就可以進(jìn)行如下修改:

public interface UserService extends IService<User>{
	
}

@Service
public class UserServiceImpl extends ServiceImpl<UserDao, User> implements UserService{

}

修改以后的好處是,MP已經(jīng)幫我們把業(yè)務(wù)層的一些基礎(chǔ)的增刪改查都已經(jīng)實(shí)現(xiàn)了,可以直接進(jìn)行使用。

編寫測試類進(jìn)行測試:

@SpringBootTest
class Mybatisplus04GeneratorApplicationTests {

    private IUserService userService;

    @Test
    void testFindAll() {
        List<User> list = userService.list();
        System.out.println(list);
    }

}

注意: 項(xiàng)目中對于MyBatis的環(huán)境是沒有進(jìn)行配置,如果想要運(yùn)行,需要提取將配置文件中的內(nèi)容進(jìn)行完善后在運(yùn)行。

思考:在MP封裝的Service層都有哪些方法可以用?

查看官方文檔:https://mp.baomidou.com/guide/crud-interface.html,這些提供的方法大家可以參考官方文檔進(jìn)行學(xué)習(xí)使用,方法的名稱可能有些變化,但是方法對應(yīng)的參數(shù)和返回值基本類似。

筆記來自:黑馬程序員SSM框架教程

3,圖書推薦

Java虛擬機(jī)核心技術(shù)一本通:通過實(shí)戰(zhàn)案例+執(zhí)行效果圖+核心代碼,剖析探索JVM核心底層原理,強(qiáng)化推動JVM優(yōu)化落地,手把手教你吃透Java虛擬機(jī)深層原理!

? 系統(tǒng):全書內(nèi)容層層遞進(jìn),深入淺出,手把手教你吃透JVM虛擬機(jī)核心技術(shù)
? 深入:剖析探索JVM核心底層原理,強(qiáng)化推動JVM優(yōu)化落地
? 實(shí)戰(zhàn):原理與實(shí)踐相結(jié)合,懂理論,能落地,實(shí)戰(zhàn)化案例精準(zhǔn)定位技術(shù)細(xì)節(jié)
? 資源:附贈全書案例源代碼,知其然更知其所以然,快速上手不用愁

【MyBatis-Plus】DML編程控制 代碼生成器(文末贈書),SSM,mybatis,java,spring,spring boot,mysql

本書主要以 Java 虛擬機(jī)的基本特性及運(yùn)行原理為中心,深入淺出地分析 JVM 的組成結(jié)構(gòu)和底層實(shí)現(xiàn),介紹了很多性能調(diào)優(yōu)的方案和工具的使用方法。最后還擴(kuò)展介紹了 JMM 內(nèi)存模型的實(shí)現(xiàn)原理和 Java 編譯器的優(yōu)化機(jī)制,讓讀者不僅可以學(xué)習(xí) JVM 的核心技術(shù)知識,還能夯實(shí) JVM 調(diào)優(yōu)及代碼優(yōu)化的技術(shù)功底。
本書適合已具有一定 Java 編程基礎(chǔ)的開發(fā)人員、項(xiàng)目經(jīng)理、架構(gòu)師及性能調(diào)優(yōu)工程師參考閱讀,同時(shí),本書還可以作為廣大職業(yè)院校、計(jì)算機(jī)培訓(xùn)班相關(guān)專業(yè)的教學(xué)參考用書。

? 《深入淺出Java虛擬機(jī):JVM原理與實(shí)戰(zhàn)》免費(fèi)包郵送4本!
?活動時(shí)間:截止到 2023-07-14 20:00:00
? 抽獎方式:利用程序進(jìn)行抽獎。
?參與方式:關(guān)注博主、點(diǎn)贊、收藏、評論區(qū)評論 “ 人生苦短,我愛java”
?本次活動一共贈書4本,評論區(qū)抽取4位小伙伴免費(fèi)送出!!
? 活動截止時(shí)間: 2023-07-14 20:00:00
?中獎名單?
vⅤ_Leon
等一下就回家℡
南 陽
小劉在C站文章來源地址http://www.zghlxwxcb.cn/news/detail-542590.html

到了這里,關(guān)于【MyBatis-Plus】DML編程控制 代碼生成器(文末贈書)的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包