04 MyBatis完成CRUD的两种方式+用POJO/Map传参输入+起别名+命名空间+输出数据

准备工作
○ 创建module(Maven的普通Java模块):mybatis-002-crud
○ pom.xml
■ 打包方式jar
■ 依赖:
● mybatis依赖
● mysql驱动依赖
● junit依赖
● logback依赖
○ mybatis-config.xml放在类的根路径下
○ CarMapper.xml放在类的根路径下(一张表对应一个Mapper)
○ logback.xml放在类的根路径下
○ 提供com.sunsplanter.mybatis.utils.SqlSessionUtil工具类
○ 创建测试用例:com.sunsplanter.mybatis.CarMapperTest


在这里插入图片描述

MyBatis 中,既可以在 Mapper XML 文件中编写 SQL,也可以在 Mapper 接口中通过注解编写 SQL。

  • 对于复杂, 频繁变更, 涉及动态SQL的 SQL 语句,使用 XML 文件可能更合适;
  • 对于简单的 SQL 语句,使用注解可能更方便
  • 此外, 调试或优化 SQL 语句时,拥有一个独立的 XML 文件可以让这个过程更加直观和集中。

1.在mapper.xml中编写SQL完成RUD

1.1.insert(Create)

1.1.1 使用map传参完成insert

在第一个mybatis程序中的CarMapper.xml文件中,Sql语句是写死的:

  <insert id="insertCar">
  //插入的东西全部写死了,但实际使用中,我们不可能预知用户需要insert的数据
  //一定是通过某种方法留空,然后前端的form表单传来数据,再将数据与sql语句结合提交
        insert into t_car(id,car_num,brand,guide_price,produce_time,car_type)
        values(null,'1003','toyota',30.00,'2000-10-11','燃油车')
    </insert>

在JDBC中,我们是这样留空的:

// JDBC中使用 ? 作为占位符。
String sql = "insert into t_car(car_num,brand,guide_price,produce_time,car_type) values(?,?,?,?,?)";

在MyBatis中,我们这样做:
在Java程序中,将数据放到Map集合中
在sql语句中使用 #{map集合的key} 来 完成传值,#{} 等同于JDBC中的 ? ,#{}就是占位符

package com.sunsplanter.mybatis;

import com.sunsplanter.mybatis.utils.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.HashMap;
import java.util.Map;

public class CarMapperTest {
    @Test
    public void testInsertCar() {
        //前端传来数据了,用map将这些数据封装起来
        Map<String, Object> map = new HashMap<>();
        map.put("k1", "103");
        map.put("k2", "奔驰E300L");
        map.put("k3", 50.3);
        map.put("k4", "2020-10-01");
        map.put("k5", "燃油车");
        //调用封装好的方法获取SqlSession对象
        SqlSession sqlSession = SqlSessionUtil.openSession();
// 执行SQL语句,第一个参数是sql段的id,第二参数是要传入的对象(使用map集合给sql语句传递数据)
        int count = sqlSession.insert("insertCar", map);
        System.out.println("插入了几条记录:" + count);
    }
}
<?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="TBD
">
    <insert id="insertCar">
<!--t_car后接的参数指明要插入表中的哪个单元,而#{} 的里面必须填写map集合的key,以正确获取map的value-->
        insert into t_car(car_num,brand,guide_price,produce_time,car_type) values(#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})
    </insert>
</mapper>

1.1.2使用POJO传参完成insert

如果采用POJO传参,#{} 里写的是get方法的方法名去掉get之后将剩下的单词首字母变小写(例如:getAge对应的是#{age},getUserName对应的是#{userName}),如果这样的get方法不存在会报错。

● 第一步:新建一个pojo包,包下定义一个pojo类Car,提供相关属性。

package com.sunsplanter.mybatis.pojo;

public class Car {
//long还是Long?若使用包装类,其提供了更多的方法可供调用,并且支持值为null的情况。
    private Long id;
    private String carNum;
    private String brand;
    private Double guidePrice;
    private String produceTime;
    private String carType;

    @Override
    public String toString() {
        return "Car{" +
                "id=" + id +
                ", carNum='" + carNum + '\'' +
                ", brand='" + brand + '\'' +
                ", guidePrice=" + guidePrice +
                ", produceTime='" + produceTime + '\'' +
                ", carType='" + carType + '\'' +
                '}';
    }

    public Car() {
    }

    public Car(Long id, String carNum, String brand, Double guidePrice, String produceTime, String carType) {
        this.id = id;
        this.carNum = carNum;
        this.brand = brand;
        this.guidePrice = guidePrice;
        this.produceTime = produceTime;
        this.carType = carType;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getCarNum() {
        return carNum;
    }

    public void setCarNum(String carNum) {
        this.carNum = carNum;
    }

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public Double getGuidePrice() {
        return guidePrice;
    }

    public void setGuidePrice(Double guidePrice) {
        this.guidePrice = guidePrice;
    }

    public String getProduceTime() {
        return produceTime;
    }

    public void setProduceTime(String produceTime) {
        this.produceTime = produceTime;
    }

    public String getCarType() {
        return carType;
    }

    public void setCarType(String carType) {
        this.carType = carType;
    }
}
@Test
public void testInsertCarByPOJO(){
	    // 创建POJO,封装数据
	    Car car = new Car();
	//前端传来数据了,用set方法将这些数据传入POJO属性
	    car.setCarNum("103");
	    car.setBrand("奔驰C200");
	    car.setGuidePrice(33.23);
	    car.setProduceTime("2020-10-11");
	    car.setCarType("燃油车");
	    //调用封装好的方法获取SqlSession对象
	    SqlSession sqlSession = SqlSessionUtil.openSession();
	// 执行SQL语句。第一个参数是sql段的id,第二参数是要传入的对象(使用POJO对象给sql语句传递数据)
	    int count = sqlSession.insert("insertCarByPOJO", car);
	    System.out.println("插入了几条记录" + count);
}
<insert id="insertCarByPOJO">
  <!--#{} 里写的是POJO的属性名-->
  insert into t_car(car_num,brand,guide_price,produce_time,car_type) values(#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})
</insert>

1.2.Delete

需求:根据car_num进行删除。

@Test
public void testDeleteByCarNum(){
    //调用封装好的方法获取SqlSession对象
    SqlSession sqlSession = SqlSessionUtil.openSession();
    // 执行SQL语句。第一个参数是sql段的id,第二个参数是要删除的“car_num”
    int count = sqlSession.delete("deleteByCarNum", "102");
    System.out.println("删除了几条记录:" + count);
}
<delete id="deleteByCarNum">
<!--当占位符只有一个的时候,${} 里面的内容可以随便写。-->
  delete from t_car where car_num = #{SuiBianXie}
</delete>

1.3.1 用POJO传参Update

需求:修改id=34的Car信息,car_num为102,brand为比亚迪汉,guide_price为30.23,produce_time为2018-09-10,car_type为电车

<update id="updateCarByPOJO">
  update t_car set 
    car_num = #{carNum}, brand = #{brand}, 
    guide_price = #{guidePrice}, produce_time = #{produceTime}, 
    car_type = #{carType} 
  where id = #{id}
</update>
  @Test
    public void testUpdateCarById_POJO(){
        // 准备数据
        Car car = new Car();
        car.setId(5L);
        car.setCarNum("102");
        car.setBrand("比亚迪汉");
        car.setGuidePrice(30.23);
        car.setProduceTime("2018-09-10");
        car.setCarType("电车");
        // 获取SqlSession对象
        SqlSession sqlSession = SqlSessionUtil.openSession();
        // 执行SQL语句
        int count = sqlSession.update("updateCarById_Map", car);
        System.out.println("更新了几条记录:" + count);
    }
1.3.2 用Map传参Update

项目结构:
在这里插入图片描述
实体类不必再说;
Mapper接口中抽象方法的声明(Mybatis 中的 Mapper 接口相当于以前的 Dao。但是区别在于,Mapper 仅仅只是建接口即可,我们不需要提供实现类,具体的SQL写到对应的Mapper文件)

EmployeeMapper接口:

int updateEmployeeByMap(Map<String, Object> paramMap);

SQL语句

<update id="updateEmployeeByMap">

  update t_emp set emp_salary=#{empSalaryKey} where emp_id=#{empIdKey}

</update>

测试程序

@Test
public void testUpdateEmpNameByMap() {
 // 获取SqlSession对象
  SqlSession sqlSession = SqlSessionUtil.openSession();
  EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
  Map<String, Object> paramMap = new HashMap<>();
  paramMap.put("empSalaryKey", 999.99);
  paramMap.put("empIdKey", 5);
  int result = mapper.updateEmployeeByMap(paramMap);
  log.info("result = " + result);
}

1.4 select

1.4.1 select(Retrieve)一条

select语句和其它语句不同的是:查询会返回一个结果集ResultSet。,然而,语句是:

Object car = sqlSession.selectOne("selectCarById", 1);

因此,必须将ResultSet转化成成一个Java对象。mybatis查询之后返回一个Java对象的话,至少你要告诉mybatis返回一个什么类型的Java对象,可以在标签中添加resultType属性,用来指定查询要转换的类型:


<select id="selectCarById" resultType="com.sunsplanter.mybatis.pojo.Car">
  select * from t_car where id = #{id}
</select>
    @Test
    public void testSelectCarById(){
        // 获取SqlSession对象
        SqlSession sqlSession = SqlSessionUtil.openSession();
        //查一条记录用selectOne方法
        Car car = sqlSession.selectOne("selectCarById", 1);
        System.out.println(car);
    }

然而,这样输出的结果是:
在这里插入图片描述

原因是:例如对于汽车编号这个字段,在POJO类和mapper文件中为carNum,但在数据库表中为car_num,这样当然查不到。
改为:

<!--虽然结果是List集合,但是resultType属性需要指定的是List集合中元素的类型。-->
<select id="selectCarById" resultType="com.sunsplanter.mybatis.pojo.Car">
  select 
   <!--记得使用as起别名,让查询结果的字段名和java类的属性名对应上。
       方法是:数据库中字段名 as Java中属性名-->
    id, car_num as carNum, brand, guide_price as guidePrice, produce_time as produceTime, car_type as carType 
  from 
    t_car 
  where 
    id = #{id}
</select>

1.4.2 select(Retrieve)多条**

  • 取一条时,用selectOne方法返回ResultSet,转化成一个指定对象,这个指定对象只要定义个Car car接受即可。
    取多条时,用selectList方法返回多个ResultSet转换成多个指定对象,这多个对象定义一个List来接受,否则要定义Car car1 car2…
<!--虽然结果是List集合,但是resultType属性需要指定的是List集合中元素的类型。-->
<select id="selectCarAll" resultType="com.sunsplanter.mybatis.pojo.Car">
  <!--记得使用as起别名,让查询结果的字段名和java类的属性名对应上。-->
  select
    id, car_num as carNum, brand, guide_price as guidePrice, produce_time as produceTime, car_type as carType
  from
    t_car
</select>
@Test
public void testSelectCarAll(){
    // 获取SqlSession对象
    SqlSession sqlSession = SqlSessionUtil.openSession();
    // 执行SQL语句
    List<Object> cars = sqlSession.selectList("selectCarAll");
    // 输出结果
    cars.forEach(car -> System.out.println(car));
}

2 (Mapper接口中编写SQL)完成CRUD

2.1 实体类:

@Data
public class Article {
private Integer id;
private Integer userId;
private String title;
private String summary;
private Integer readCount;
private LocalDateTime createTime;
private LocalDateTime updateTime;

2.2 ArticleMapper.java接口:

public interface ArticleMapper {
	String
field_list="id,user_id,title,summary,read_count,create_time,update_time";

	@Insert("""
		insert into
article(user_id,title,summary,read_count,create_time,update_time) \
values(#{userId},#{title},#{summary},#{readCount},#{createTime},#{updateTime})
""")
	int insertArticle(Article article);

	@Update("""
		update article set read_count=#{num} where id=#{id}
		""")
	int updateReadCount(Integer id,Integer num);

	@Delete("""
		delete from article where id=#{id}
		""")
	int deleteById(Integer id);

	/*
	*若将SQL写入mapper接口,一个查询操作应包含三部分内容
	1.SQL语句  2.结果映射  3.方法声明
	*3: 将实体类字段articleId映射到数据库字段id, 调用selectById方法并传入参数, 最终返回一个Article对象
	*/
	@Select("select " + field_list + " from article where id=#{articleId}")
	@Results({
		@Result(id = true,column = "id",property = "id"),
		@Result(column = "user_id",property = "userId"),
		@Result(column = "read_count",property = "readCount"),
		@Result(column = "create_time",property = "createTime"),
		@Result(column = "update_time",property = "updateTime"),
	})
	Article selectById(@Param("articleId") Integer id)
}

2.3 启动类加入扫描注解, 配置数据源

	@MapperScan({"com.sunsplanter.mapper"})
	@SpringBootApplication
	public class Lession10MyBatisApplication {
	public static void main(String[] args) {
		SpringApplication.run(Lession10MyBatisApplication.class, args);
	}
}

2.4 单元测试

	@SpringBootTest
	class MyBatisApplicationTests {

	@Autowired
	private ArticleMapper articleMapper;

	@Test
	void testInsert() {
		ArticlePO article = new ArticlePO();
		article.setTitle("什么时候用微服务");
		article.setSummary("微服务优缺点");
		article.setUserId(219);
		article.setReadCount(560);
		article.setCreateTime(LocalDateTime.now());
		article.setUpdateTime(LocalDateTime.now());
		articleMapper.insertArticle(article);
}

	@Test
	void testUpdate() {
		int rows = articleMapper.updateReadCount(1, 230);
		System.out.println("修改的记录数量:" + rows);
	}

	@Test
	void testDelete(){
		int rows = articleMapper.deleteById(11);
		System.out.println("删除记录的数量 " + rows);
	}
	@Test
	void testSelect(){
		ArticlePO articlePO = articleMapper.selectById(3);
		System.out.println("查询到的文章:" + articlePO);
	}
}

2.5 (Mapper接口中编写SQL)的优化

尽管我们能在方法上面直接编写 SQL 语句 , 但不够简洁。
MyBatis 提供了 SQL 提供者的功能。将 SQL 以方法的形式定义在单独的类中。 Mapper 接口通过引用 SQL 提供
者中的方法名称,声明要执行的 SQL。
SQL 提供者有四类@SelectProvider,@InsertProvider,@UpdateProvider,@DeleteProvider。
其格式是: @Select/Insert…Provider(type = 提供者类.class, method = “方法名称”)

2.5.1 创建 SQL 提供者

	public class SqlProvider {

	public static String selectArticle(){
		return """
			select id,user_id,title,summary,read_count,create_time,update_time
			from article where id=#{articleId}
			""";
	}
}

2.5.2 创建 mapper 接口

public interface ArticleMapper {
@Select("")
@Results(id = "BaseMapper", value = {
@Result(id = true, column = "id", property = "id"),
@Result(column = "user_id", property = "userId"),
@Result(column = "read_count", property = "readCount"),
@Result(column = "create_time", property = "createTime"),
@Result(column = "update_time", property = "updateTime"),
})
Article articleMapper();

//查询
@ResultMap("BaseMapper")
@SelectProvider(type = SqlProvider.class,method = "selectArticle")
Article selectById(Integer id);

}

3 起别名简化xxxMapper中的resultType

在UserMapper.xml中, 如果返回的类型指定为一个已定义类, 则resultType会比较繁琐:

  <select id="selectById" resultType="com.sunsplanter.pojo.User">
    SELECT id, username, password FROM user WHERE id=#{id}
  </select>

解决办法: 在mybatis-config.xml中提前用typeAliases标签起别名:

    <typeAliases>
        <typeAlias type="com.sunsplanter.pojo.User" alias="User"/>
    </typeAliases>

此时UserMapper.xml中可以改为:

  <select id="selectById" resultType="User">
    SELECT id, username, password FROM user WHERE id=#{id}
  </select>

5 命名空间

在SQL Mapper配置文件中标签的namespace属性可以翻译为命名空间,这个命名空间主要是为了防止sql id冲突的。
我们知道一张表对应一个Mapper.xml,而在程序中,仅仅以语句块的id作为辨识符,决定执行哪个语句。例如:
CarMapper1.xml:

<mapper namespace="car1">
<select id="selectCarAll" resultType="com.sunsplanter.mybatis.pojo.Car">
  select
    id, car_num as carNum, brand, guide_price as guidePrice, produce_time as produceTime, car_type as carType
  from
    t_car
</select>
</mapper>

此时如有CarMapper2.xml:

<mapper namespace="car2">
<select id="selectCarAll" resultType="com.sunsplanter.mybatis.pojo.Car">
  select
    id, car_num as carNum, brand, guide_price as guidePrice, produce_time as produceTime, car_type as carType
  from
    t_car
</select>
</mapper>

而代码为:

@Test
public void testNamespace(){
    // 获取SqlSession对象
    SqlSession sqlSession = SqlSessionUtil.openSession();
    // 执行SQL语句
    List<Object> cars = sqlSession.selectList("selectCarAll");
    // 输出结果
    cars.forEach(car -> System.out.println(car));
}

就会报错,因为两个Mapper中都有id为“selectCarAll”的Sql语句段,系统无法分辨执行哪一段。
解决办法:“namespace.id”

   List<Object> cars = sqlSession.selectList("car2.selectCarAll");

6 输出数据

项目结构:
在这里插入图片描述

6.1 准备表和实体类

准备一张名为 user 的表。该表包含字段 id(主键)、username、password
准备pojo类:

@Data //lombok生成set/get/构造方法
public class User {
  private Integer id;
  private String username;
  private String password;
}

6.2 定义一个 Mapper 接口 UserMapper,并在其中添加 user 表的增、删、改、查方法。

public interface UserMapper {
  
  int insert(User user);

  int update(User user);

  int delete(Integer id);

  User selectById(Integer id);

  List<User> selectAll();
}

6.3 编写UserMapper.xml

在 resources /mappers目录下创建一个名为 UserMapper.xml 的 XML 文件,包含与 Mapper 接口中相同的五个 SQL 语句,并在其中,将查询结果映射到 User 实体中。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace等于mapper接口类的全限定名,这样实现对应 -->
<mapper namespace="com.atguigu.mapper.UserMapper">
  <!-- 定义一个插入语句,并获取主键值 -->
  <insert id="insert" useGeneratedKeys="true" keyProperty="id">
    INSERT INTO user(username, password)
                VALUES(#{username}, #{password})
  </insert>
  
  <update id="update">
    UPDATE user SET username=#{username}, password=#{password}
    WHERE id=#{id}
  </update>
  
  <delete id="delete">
    DELETE FROM user WHERE id=#{id}
  </delete>
  <!-- resultType使用user别名,稍后需要配置!-->
  <select id="selectById" resultType="user">
    SELECT id, username, password FROM user WHERE id=#{id}
  </select>
  
  <!-- resultType返回值类型为集合,所以只写范型即可! -->
  <select id="selectAll" resultType="user">
    SELECT id, username, password FROM user
  </select>
  
</mapper>

6.4 编写mybatis-config.xml配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

    <settings>
        <!-- 开启驼峰式映射-->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
        <!-- 开启logback日志输出-->
        <setting name="logImpl" value="SLF4J"/>
    </settings>

    <typeAliases>
        <!-- 给实体类起别名 -->
        <package name="com.atguigu.pojo"/>
    </typeAliases>

    <!-- environments表示配置Mybatis的开发环境,可以配置多个环境,在众多具体环境中,使用default属性指定实际运行时使用的环境。default属性的取值是environment标签的id属性的值。 -->
    <environments default="development">
        <!-- environment表示配置Mybatis的一个具体的环境 -->
        <environment id="development">
            <!-- Mybatis的内置的事务管理器 -->
            <transactionManager type="JDBC"/>
            <!-- 配置数据源 -->
            <dataSource type="POOLED">
                <!-- 建立数据库连接的具体信息 -->
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis-example"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <!-- Mapper注册:从Resources开始查找,此时已经放进mappers文件夹了,因此写全 -->
        <mapper resource="mappers/UserMapper.xml"/>
    </mappers>
</configuration>

6.5 测试程序

package com.atguigu.test;

import com.atguigu.mapper.UserMapper;
import com.atguigu.pojo.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.util.List;

/**
 * projectName: com.atguigu.test
 */
public class MyBatisTest {

    private SqlSession sqlSession;
    // junit会在每一个@Test方法前执行@BeforeEach方法

    @BeforeEach
    public void init() throws IOException {
        sqlSession = new SqlSessionFactoryBuilder()
                .build(
                        Resources.getResourceAsStream("mybatis-config.xml"))
                .openSession();
    }

    @Test
    public void createTest() {
        User user = new User();
        user.setUsername("admin");
        user.setPassword("123456");
        UserMapper userMapper = session.getMapper(UserMapper.class);
        userMapper.insert(user);
        System.out.println(user);
    }

    @Test
    public void updateTest() {
        UserMapper userMapper = session.getMapper(UserMapper.class);
        User user = userMapper.selectById(1);
        user.setUsername("root");
        user.setPassword("111111");
        userMapper.update(user);
        user = userMapper.selectById(1);
        System.out.println(user);
    }

    @Test
    public void deleteTest() {
        UserMapper userMapper = session.getMapper(UserMapper.class);
        userMapper.delete(1);
        User user = userMapper.selectById(1);
        System.out.println("user = " + user);
    }

    @Test
    public void selectByIdTest() {
        UserMapper userMapper = session.getMapper(UserMapper.class);
        User user = userMapper.selectById(1);
        System.out.println("user = " + user);
    }

    @Test
    public void selectAllTest() {
        UserMapper userMapper = session.getMapper(UserMapper.class);
        List<User> userList = userMapper.selectAll();
        System.out.println("userList = " + userList);
    }

    // junit会在每一个@Test方法后执行@@AfterEach方法
    @AfterEach
    public void clear() {
        session.commit();
        session.close();
    }
}
  • 7
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值