Mybatis-plus實現數據庫的增刪改查操作


目錄

1、MybatisPlus簡介

2、MybatisPlus注解介紹

3、常用方法

4、SpringBoot整合MybatisPlus實現增刪改查的一個簡單Demo

5、參考資料


1、MybatisPlus簡介

Mybatis和MybatisPlus都是非常流行的持久層框架。mybatis可以直接在xml或注解中通過SQL語句操作數據庫,很是靈活。但是其操作都要通過SQL語句進行,就必須寫大量的xml文件或者注解sql語句,很是麻煩。而mybatis-plus就很好的解決了這個問題。

Mybatis-Plus(簡稱MP)是 Mybatis 的增強工具,在 Mybatis 的基礎上只做增強不做改變,為簡化開發、提高效率而生。關於mybatis-plus的更多介紹及特性,可以參考mybatis-plus官網

mybatis-plus已經封裝好了一些crud方法,我們不需要再寫SQL語句了,直接調用這些方法就行

 

2、MybatisPlus注解介紹

  • @TableId 關聯主鍵,可以通過type屬性指定是否自增
  • @TableName 通過value屬性關聯表名,當類名與表名一致時,value屬性可省寫
  • @TableField 關聯表字段,如果屬性名稱與字段名稱一致則此注解可以省寫(包含駝峰規則)

 

3、常用方法

MybatisPlus常用方法可以去看BaseMapper這個接口。BaseMapper這個接口里面封裝了一些常用的sql。

public interface BaseMapper<T> extends Mapper<T> {
    int insert(T entity);

    int deleteById(Serializable id);

    int deleteByMap(@Param("cm") Map<String, Object> columnMap);

    int delete(@Param("ew") Wrapper<T> wrapper);

    int deleteBatchIds(@Param("coll") Collection<? extends Serializable> idList);

    int updateById(@Param("et") T entity);

    int update(@Param("et") T entity, @Param("ew") Wrapper<T> updateWrapper);

    T selectById(Serializable id);

    List<T> selectBatchIds(@Param("coll") Collection<? extends Serializable> idList);

    List<T> selectByMap(@Param("cm") Map<String, Object> columnMap);

    T selectOne(@Param("ew") Wrapper<T> queryWrapper);

    Integer selectCount(@Param("ew") Wrapper<T> queryWrapper);

    List<T> selectList(@Param("ew") Wrapper<T> queryWrapper);

    List<Map<String, Object>> selectMaps(@Param("ew") Wrapper<T> queryWrapper);

    List<Object> selectObjs(@Param("ew") Wrapper<T> queryWrapper);

    <E extends IPage<T>> E selectPage(E page, @Param("ew") Wrapper<T> queryWrapper);

    <E extends IPage<Map<String, Object>>> E selectMapsPage(E page, @Param("ew") Wrapper<T> queryWrapper);
}

Wrapper為條件查詢構造器,QueryWrapper為Wrapper的一個實現方法。主要是用來進行多個where條件的拼接。

例如:

QueryWrapper<Student> queryWrapper = new QueryWrapper<>();
queryWrapper.gt("age", 10).eq("sex", "女"); //年齡大於10且性別為“女"
Wrapper中的方法 含義
ge
大於等於
gt
表示大於
le
小於等於
lt
小於
eq
等於
ne
不等於
between
范圍 between("age",20,30); 年齡范圍[20,30]

 

4、SpringBoot整合MybatisPlus實現增刪改查的一個簡單Demo

源碼:https://gitee.com/wulinchun/mybatis-plus.git   (dev分支)

4.1、目錄結構

 

4.2、代碼

4.2.1、xml&yml配置

pom.xml

<?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 http://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.2.2.RELEASE</version>
        <relativePath/>
    </parent>
    <groupId>org.example</groupId>
    <artifactId>mybatis-plus</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--Mybatis-Plus -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.0</version>
        </dependency>
        <!--mybatis-plus 代碼生成器-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.4.0</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.20</version>
            <scope>provided</scope>
        </dependency>
        <!--sqlserver依賴-->
        <dependency>
            <groupId>com.microsoft.sqlserver</groupId>
            <artifactId>mssql-jdbc</artifactId>
        </dependency>
        <!--mysql依賴-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.26</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <fork>true</fork>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

application.yml

server:
  port: 8080

spring:
  profiles:
    active: dev  #使用application-dev.yml里面的配置

application-dev.yml

server:
  port: 8080
spring:
  datasource:
    name: mybatis_plus
    url: jdbc:mysql://localhost:3306/mydb?serverTimezone=UTC&characterEncoding=utf8
    username: root
    password: 123456
    driver-class-name: com.mysql.jdbc.Driver
mybatis:
  mapper-locations: classpath:mapper/*.xml #注意:一定要對應mapper映射xml文件的所在路徑
  type-aliases-package: com.mybatisplus.entity # 注意:對應實體類的路徑

mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #打印mybatis-plus的sql語句

ScoreMapper.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">
<mapper namespace="com.mybatisplus.mapper.IScoreMapper">

</mapper>

StudentMapper.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">
<mapper namespace="com.mybatisplus.mapper.IStudentMapper">

</mapper>

 

4.2.2、實體類

Score.java

package com.mybatisplus.entity;

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @author: wu linchun
 * @time: 2021/6/6 11:34
 * @description:
 */
@Data
@TableName("t_score")
@NoArgsConstructor
@AllArgsConstructor
public class Score {
    @TableField("sno")
    private int sno;
    @TableField("subject")
    private String subject;
    @TableField("score")
    private int score;
}

Student.java

package com.mybatisplus.entity;

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

/**
 * @author: Wu Linchun
 * @date: 2021/06/04/10:25
 * @Description:
 **/
@Data
@TableName("t_student")
@NoArgsConstructor
@AllArgsConstructor
public class Student implements Serializable {
    @TableId
    @TableField("sno")
    private int sno;
    @TableField("sname")
    private String sname;
    @TableField("age")
    private int age;
    @TableField("sex")
    private String sex;
    @TableField("sclass")
    private String sclass;
}
StudentScoreVO.java 注:該實體類為映射t_student表和t_score表聯合查詢的結果
package com.mybatisplus.entity.vo;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

/**
 * @author: wu linchun
 * @time: 2021/6/6 11:42
 * @description:
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class StudentScoreVO implements Serializable {
    @TableField("sno")
    private int sno;
    @TableField("sname")
    private String sname;
    @TableField("subject")
    private String subject;
    @TableField("score")
    private int score;
}

 

4.2.3、接口&實現類

IScoreMapper.java
package com.mybatisplus.mapper;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.mybatisplus.entity.Student;
import com.mybatisplus.entity.vo.StudentScoreVO;
import javafx.scene.control.Pagination;
import org.apache.ibatis.annotations.Select;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * @description:
 * @author: wu linchun
 * @time: 2021/6/6 11:49
 */
@Component("iScoreMapper")
public interface IScoreMapper extends BaseMapper<StudentScoreVO> {
    /**
     * 獲取學生成績並分頁顯示
     * @return
     */
    @Select("select tst.sno,tst.sname,tsc.subject,tsc.score from t_student tst,t_score tsc where tst.sno=tsc.sno")
    List<StudentScoreVO> getScore();


    /**
     * 分頁顯示學生成績
     * @param
     * @return
     */
    @Select("select tst.sno,tst.sname,tsc.subject,tsc.score from t_student tst,t_score tsc where tst.sno=tsc.sno")
    IPage<StudentScoreVO> getByPage(Page<StudentScoreVO> studentScoreVOPage,QueryWrapper<StudentScoreVO> queryWrapper);


    @Select("select count(*) from t_student tst,t_score tsc where tst.sno=tsc.sno")
    int getCount();
}
IStudentMapper.java
package com.mybatisplus.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.mybatisplus.entity.Student;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Component;

/**
 * @author: Wu Linchun
 * @date: 2021/06/04/14:48
 * @Description:
 **/
@Component("iStudentMapper")
public interface IStudentMapper extends BaseMapper<Student> {
}
IScoreService.java
package com.mybatisplus.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.mybatisplus.entity.vo.StudentScoreVO;
import javafx.scene.control.Pagination;

import java.util.List;

/**
 * @description:
 * @author: wu linchun
 * @time: 2021/6/6 11:55
 */

public interface IScoreService {
    /**
     *
     * @return
     */
    List<StudentScoreVO> getScore();

    /**
     *分頁顯示
     * @param
     * @return
     */
    IPage<StudentScoreVO> getByPage(Page<StudentScoreVO> studentScoreVOPage, QueryWrapper<StudentScoreVO> queryWrapper);

    /**
     *
     * @return
     */
    int getCount();
}
IStudentService.java
package com.mybatisplus.service;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.mybatisplus.entity.Student;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;

import java.util.List;

/**
 * @author: Wu Linchun
 * @date: 2021/06/04/14:02
 * @Description:
 **/

public interface IStudentService {
    /**
     * 根據唯一性主鍵進行查詢  sno是唯一性主鍵
     *
     * @param sno
     * @return
     */
    Student getBySno(int sno);

    /**
     * 查詢全部
     *
     * @return
     */
    List<Student> getAll();

    /**
     * 根據性別和年齡查詢
     *
     * @param sex
     * @param age
     * @return
     */
    List<Student> getBySexAge(String sex, int age);

    /**
     * 范圍&多條件查詢
     *
     * @param age
     * @return
     */
    List<Student> getStudentsByScope(int age);

    /**
     * 添加學生
     *
     * @param student
     * @return
     */
    int insertStudent(Student student);

    /**
     * 根據指定條件刪除
     *
     * @param sno
     * @return
     */
    int deleteBySno(int sno);

    /**
     * 根據多個指定條件刪除
     *
     * @param sclass
     * @param sex
     * @return
     */
    int deleteBySclass_sex(String sclass, String sex);

    /**
     * 更新
     *
     * @param student
     * @return
     */
    int updateStudent(Student student);

    /**
     * 獲取表中總記錄數
     * @return
     */
    int getCount();

}
ScoreServiceImpl.java
package com.mybatisplus.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.mybatisplus.entity.vo.StudentScoreVO;
import com.mybatisplus.mapper.IScoreMapper;
import com.mybatisplus.service.IScoreService;
import javafx.scene.control.Pagination;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * @author: wu linchun
 * @time: 2021/6/6 11:56
 * @description:
 */
@Service
public class ScoreServiceImpl implements IScoreService {
    @Autowired
    @Qualifier("iScoreMapper")
    private IScoreMapper iScoreMapper;


    @Override
    public List<StudentScoreVO> getScore() {
        return iScoreMapper.getScore();
    }

    @Override
    public IPage<StudentScoreVO> getByPage(Page<StudentScoreVO> studentScoreVOPage, QueryWrapper<StudentScoreVO> queryWrapper) {
        return iScoreMapper.getByPage(studentScoreVOPage, queryWrapper);
    }


    @Override
    public int getCount() {
        return iScoreMapper.getCount();
    }
}
StudentServiceImpl.java
package com.mybatisplus.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.mybatisplus.entity.Student;
import com.mybatisplus.mapper.IStudentMapper;
import com.mybatisplus.service.IStudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author: Wu Linchun
 * @date: 2021/06/04/14:55
 * @Description:
 **/
@Service
public class StudentServiceImpl implements IStudentService {

    @Autowired
    @Qualifier("iStudentMapper")
    private IStudentMapper iStudentMapper;


    @Override
    public Student getBySno(int sno) {
        return iStudentMapper.selectById(sno);
    }

    @Override
    public List<Student> getAll() {
        return iStudentMapper.selectList(null);
    }


    @Override
    public List<Student> getBySexAge(String sex, int age) {
        Map<String, Object> map = new HashMap<>();
        map.put("sex", sex);
        map.put("age", age);
        return iStudentMapper.selectByMap(map);
    }

    @Override
    public List<Student> getStudentsByScope(int age) {
        QueryWrapper<Student> queryWrapper = new QueryWrapper<>();
        //ge表示大於等於、gt表示大於、le表示小於等於、lt表示小於
        //eq等於、ne不等於
        //between范圍 between("age",20,30); 年齡范圍20~30
        //查詢年齡20-30范圍 1.代表字段  2.代表開始值  3.代表結束值
       /* queryWrapper.gt("age",age);

        queryWrapper.gt("age",10); //年齡大於10
        queryWrapper.ge("age",10); //年齡大於等於10
        queryWrapper.le("age",10); //年齡小於等於10
        queryWrapper.lt("age",10); //年齡小於10
        queryWrapper.eq("age",10); //年齡等於10
        queryWrapper.ne("age",10); //年齡不等於10
        queryWrapper.between("age",20,30); //年齡介於20~30之間*/
        queryWrapper.gt("age", 10).eq("sex", "女"); //年齡大於10且性別為“女"
        return iStudentMapper.selectList(queryWrapper);

    }

    @Override
    public int insertStudent(Student student) {
        return iStudentMapper.insert(student);
    }


    @Override
    public int deleteBySno(int sno) {
        QueryWrapper<Student> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("sno", sno);
        return iStudentMapper.delete(queryWrapper);
    }

    /**
     * 根據班級和性別刪除
     *
     * @param sclass
     * @param sex
     * @return
     */
    @Override
    public int deleteBySclass_sex(String sclass, String sex) {
        Map<String, Object> map = new HashMap<>();
        map.put("sclass", sclass);
        map.put("sex", sex);
        return iStudentMapper.deleteByMap(map);
    }

    @Override
    public int updateStudent(Student student) {
        QueryWrapper<Student> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("sno", student.getSno());
        return iStudentMapper.update(student, queryWrapper);
    }

    @Override
    public int getCount() {
        QueryWrapper<Student> queryWrapper=new QueryWrapper<>();
        queryWrapper.eq("sex","男");
       // return iStudentMapper.selectCount(null);  //null為獲取表中總記錄數
        return iStudentMapper.selectCount(queryWrapper);  //獲取指定條件的記錄數
    }
}

 

4.2.4、配置類

MybatisPlusConfig.java
package com.mybatisplus.config;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author: wu linchun
 * @time: 2021/6/6 12:13
 * @description: mybatis-plus分頁插件配置
 */
@Configuration
public class MybatisPlusConfig {
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        return new PaginationInterceptor();
    }

}

 

4.2.5、測試類

MyControllerTest.java
package com.mybatisplus.test;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.mybatisplus.entity.Student;
import com.mybatisplus.entity.vo.StudentScoreVO;
import com.mybatisplus.service.IStudentService;
import com.mybatisplus.service.impl.ScoreServiceImpl;
import com.mybatisplus.service.impl.StudentServiceImpl;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.web.WebAppConfiguration;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author: wu linchun
 * @time: 2021/6/5 20:31
 * @description:
 */
@RunWith(SpringRunner.class)
@SpringBootTest
//由於是Web項目,Junit需要模擬ServletContext,因此我們需要給我們的測試類加上@WebAppConfiguration。
@WebAppConfiguration
public class MyControllerTest {
    @Autowired
    @Qualifier("studentServiceImpl")
    private StudentServiceImpl studentServiceImpl;

    @Autowired
    @Qualifier("scoreServiceImpl")
    private ScoreServiceImpl scoreServiceImpl;

    @Test
    public void testAdd() {
        int sno = 20160001;
        for (int i = 0; i < 10; i++) {
            Student student = new Student(sno++, "張三", 20, "男", "2016222");
            if (studentServiceImpl.insertStudent(student) == 1) {
                System.out.println("添加成功");
            } else {
                System.out.println("添加失敗");
            }
        }
    }

    @Test
    public void testDeleteBySno() {
        if (studentServiceImpl.deleteBySno(20160009) == 1) {
            System.out.println("刪除成功");
        } else {
            System.out.println("刪除失敗");
        }
    }

    @Test
    public void testDeleteBySclass_sex() {
        if (studentServiceImpl.deleteBySclass_sex("2016221", "男") == 1) {
            System.out.println("刪除成功");
        } else {
            System.out.println("刪除失敗");
        }
    }

    @Test
    public void testGetAll() {
        List<Student> studentList = new ArrayList<Student>();
        studentList = studentServiceImpl.getAll();
        for (Student student : studentList) {
            System.out.println(studentList.toString());
        }
    }

    @Test
    public void testGetStudentsByScope() {
        List<Student> studentList = studentServiceImpl.getStudentsByScope(10);
        for (Student student : studentList) {
            System.out.println(studentList.toString());
        }
    }

    @Test
    public void testUpdateStudent() {
        Student student = new Student(20160010, "HHH", 20, "男", "2016222");
        if (studentServiceImpl.updateStudent(student) == 1) {
            System.out.println("更新成功");
        } else {
            System.out.println("更新失敗");
        }
    }

    @Test
    public void testGetBySno() {
        Student student = studentServiceImpl.getBySno(20160003);
        System.out.println(student.toString());
    }

    @Test
    public void testGetCount() {
        System.out.println(studentServiceImpl.getCount());
    }

    @Test
    public void testGetScore() {
        List<StudentScoreVO> studentScoreVOList = scoreServiceImpl.getScore();
        for (StudentScoreVO studentScoreVO : studentScoreVOList) {
            System.out.println(studentScoreVO.toString());
        }
    }

    @Test
    public void testGetByPage() {
        int curr = 0, size = 3;
        while (curr <= scoreServiceImpl.getCount() / 3 + 1) {
            Page<StudentScoreVO> page = new Page<>(curr, size);
            System.out.println(curr + " " + size);
            scoreServiceImpl.getByPage(page, null).getRecords().forEach(System.out::println);
            curr++;
            System.out.println("------------------------------------------------");
        }
    }

}

由於在yml中增加了打印MybatisPlus的sql配置,因此執行時可以在控制台看到輸出的MybatisPlus封裝的sql語句。

 

5、參考資料

MyBatis Plus 簡單介紹 - 知乎 (zhihu.com)

mybatis plus實現某種條件查詢 - 謝世林 - 博客園 (cnblogs.com)

mybatis-plus的sql語句打印問題_猿,碼的博客-CSDN博客_mybatisplus打印sql語句

mybatis-plus實現多表聯查 - 白衣風雲 - 博客園 (cnblogs.com)

MyBatis-Plus_分頁查詢_Gblfy_Blog-CSDN博客_mybatis-plus分頁查詢

Mybatis-plus中sql語句LT、LE、EQ、NE、GE、GT的意思 - 潘向福 - 博客園 (cnblogs.com)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM