Mybatis实现增删改查(CRUD)

一、mybatis 环境搭建步骤

  • 第一步:创建maven工程
  • 第二步:导入坐标
  • 第三步:编写实体类和持久层接口
  • 第四步:编写SqlMapConfig.xml
  • 第五步:编写映射配置文件
  • 第六步:编写测试类
  • mybatis创建过程

二、 基于代理 Dao 实现 CRUD 操作

1、编写测试类

/**
 * @author GaoYang
 * @Company http://www.geq2020.top
 * @className: MybatisTest
 * @Description: TODO
 */
public class MybatisTest {
    
    
    private InputStream input;
    private SqlSession sqlSession;
    private IUserDao userDao;
    // @Before 用于在测试方法执行之前执行
    @Before
    public  void init() throws Exception {
    
    
        input = Resources.getResourceAsStream("SqlMapConfig.xml");
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(input);
        sqlSession =factory.openSession(true);
        userDao = sqlSession.getMapper(IUserDao.class);
    }
    //  释放资源 
	// @After 在测试方法之后执行
    @After
    public void closes() throws Exception {
    
    
        sqlSession.close();
        input.close();
    }
}

2、根据id查询

在持久层接口中添加 findById 方法

/**
* 根据 id 查询
* @param userId
* @return
*/
User findById(Integer userId);

在用户的映射配置文件中配置

<!-- 根据 id 查询 -->
<select id="findById" resultType="com.domain.User" parameterType="int">
select * from user where id = #{uid}
</select>
<!-- 
细节:
	resultType 属性:用于指定结果集的类型。
	parameterType 属性:用于指定传入参数的类型。
sql 语句中使用#{}字符:
	它代表占位符,相当于原来 jdbc 部分所学的?,都是用于执行语句时替换实际的数据。
	具体的数据是由#{}里面的内容决定的。
#{}中内容的写法:
	由于数据类型是基本类型,所以此处可以随意写。
-->

在测试类测试

@Test
    public void testFindById(){
    
    
        User user = userDao.findById(3);
        System.out.println(user);
    }

3、保存操作

在持久层接口中添加新增方法

/**
* 保存用户
* @param user
* @return 影响数据库记录的行数
*/
int saveUser(User user);

在用户的映射配置文件中配置

<!-- 保存用户-->
<insert id="saveUser" parameterType="com.domain.User">
	insert into user(username,birthday,sex,address)values(#{username},#{birthday},#{sex},#{address})
</insert>

细节:
	parameterType 属性:
		代表参数的类型,因为我们要传入的是一个类的对象,所以类型就写类的全名称。
	sql 语句中使用#{}字符:
		它代表占位符,相当于原来 jdbc 部分所学的?,都是用于执行语句时替换实际的数据。
		具体的数据是由#{}里面的内容决定的。
	#{}中内容的写法:
		由于我们保存方法的参数是 一个 User 对象,此处要写 User 对象中的属性名称。
		它用的是 ognl 表达式。
	ognl 表达式:
		它是 apache 提供的一种表达式语言,全称是:
		Object Graphic Navigation Language 对象图导航语言
		它是按照一定的语法格式来获取数据的。
		语法格式就是使用 #{对象.对象}的方式
	#{user.username}它会先去找 user 对象,然后在 user 对象中找到 username 属性,并调用getUsername()方法把值取出来。但是我们在 parameterType 属性上指定了实体类名称,所以可以省略 user.而直接写 username。

添加测试类中的测试方法

	@Test
	public void testSave(){
    
    
		User user = new User();
		user.setUsername("modify User property");
		user.setAddress("北京市顺义区");
		user.setSex("男");
		user.setBirthday(new Date());
		System.out.println("保存操作之前:"+user);
		//5.执行保存方法
		userDao.saveUser(user);
		System.out.println("保存操作之后:"+user);
	}

新增用户 id 的返回值

	<!--新增用户后,同时还要返回当前新增用户的 id 值,因为 id 是由数据库的自动增长来实现的,所以就相当于我们要在新增后将自动增长 auto_increment 的值返回。-->
	<insert id="saveUser" parameterType="USER">
	<!-- 配置保存时获取插入的 id -->
	<selectKey keyColumn="id" keyProperty="id" resultType="int">
	select last_insert_id();
	</selectKey>
	insert into user(username,birthday,sex,address)
	values(#{username},#{birthday},#{sex},#{address})
	</insert>

4、用户更新

在持久层接口中添加更新方法

	/**
	* 更新用户
	* @param user
	* @return 影响数据库记录的行数
	*/
	int updateUser(User user);

在用户的映射配置文件中配置

	<!-- 更新用户 -->
	<update id="updateUser" parameterType="com.domain.User">
	update user set username=#{username},birthday=#{birthday},sex=#{sex},
	address=#{address} where id=#{id}
	</update>

加入更新的测试方法

	@Test
	public void testUpdateUser()throws Exception{
    
    
	//1.根据 id 查询
	User user = userDao.findById(52);
	//2.更新操作
	user.setAddress("北京市顺义区");
	int res = userDao.updateUser(user);
	System.out.println(res);
	}

5、用户删除

在持久层接口中添加删除方法

	/**
	* 根据 id 删除用户
	* @param userId
	* @return
	*/
	int deleteUser(Integer userId);

在用户的映射配置文件中配置

	<!-- 删除用户 -->
	<delete id="deleteUser" parameterType="java.lang.Integer">
	delete from user where id = #{uid}
	</delete>

加入删除的测试方法

	@Test
	public void testDeleteUser() throws Exception {
    
    
	//6.执行操作
	int res = userDao.deleteUser(52);
	System.out.println(res);
	}

6、模糊查询

在持久层接口中添加模糊查询方法

	/**
	* 根据名称模糊查询
	* @param username
	* @return
	*/
	List<User> findByName(String username);

在用户的映射配置文件中配置

<!-- 根据名称模糊查询 -->
	<select id="findByName" resultType="com.domain.User" parameterType="String">
	select * from user where username like #{username}
	</select>

加入模糊查询的测试方法

	@Test
 	public void testFindByName(){
    
    
		 //5.执行查询一个方法
	List<User> users = userDao.findByName("%王%");
	for(User user : users){
    
    
	System.out.println(user);
		}
	 }
 //我们在配置文件中没有加入%来作为模糊查询的条件,所以在传入字符串实参时,就需要给定模糊查询的标识%。
 //配置文件中的#{username}也只是一个占位符,所以 SQL 语句显示为“?”。

模糊查询的另一种配置方式

	第一步:修改 SQL 语句的配置,配置如下:
	<!-- 根据名称模糊查询 -->
	<select id="findByName" parameterType="string" resultType="com.itheima.domain.User">
	select * from user where username like '%${value}%'
	</select>
	// 我们在上面将原来的#{}占位符,改成了${value}。注意如果用模糊查询的这种写法,那么${value}的写法就是固定的,不能写成其它名字。
	//  第二步:测试,如下:
	/**
	* 测试模糊查询操作
	*/
	@Test
	public void testFindByName(){
    
    
	//5.执行查询一个方法
	List<User> users = userDao.findByName("王");
	for(User user : users){
    
    
	System.out.println(user);
		}
	}

7、#{}与${}的区别

  • #{}表示一个占位符号
    通过#{}可以实现 preparedStatement 向占位符中设置值,自动进行 java 类型和 jdbc 类型转换,
    #{}可以有效防止 sql 注入。 #{}可以接收简单类型值或 pojo 属性值。 如果 parameterType 传输单个简单类
    型值,#{}括号中可以是 value 或其它名称。
  • 表 示 拼 接 s q l 串 : 通 过 {}表示拼接sql串: 通过 sql:{}可以将 parameterType 传入的内容拼接在 sql 中且不进行 jdbc 类型转换, 可 以 接 收 简 单 类 型 值 或 p o j o 属 性 值 , 如 果 p a r a m e t e r T y p e 传 输 单 个 简 单 类 型 值 , {}可以接收简 单类型值或 pojo 属性值,如果 parameterType 传输单个简单类型值, pojoparameterType{}括号中只能是 value。

8、查询使用聚合函数

在持久层接口中添加模糊查询方法

   **
   * 查询总记录条数
   * @return
   */
   int findTotal();

在用户的映射配置文件中配置

<!-- 查询总记录条数 -->
<select id="findTotal" resultType="int">
select count(*) from user;
</select>

加入聚合查询的测试方法

	@Test
	public void testFindTotal() throws Exception {
    
    
	//6.执行操作
	int res = userDao.findTotal();
	System.out.println(res);
	}

三、Mybatis与JDBC编程的比较

1、数据库链接创建、释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库链接池可解决此问题。
  • 解决:在 SqlMapConfig.xml 中配置数据链接池,使用连接池管理数据库链接。
2、Sql 语句写在代码中造成代码不易维护,实际应用 sql 变化的可能较大,sql 变动需要改变 java 代码。
  • 解决:将 Sql 语句配置在 XXXXmapper.xml 文件中与 java 代码分离。
3、.向 sql 语句传参数麻烦,因为 sql 语句的 where 条件不一定,可能多也可能少,占位符需要和参数对应。
  • 解决:Mybatis 自动将 java 对象映射至 sql 语句,通过 statement 中的 parameterType 定义输入参数的
    类型
4、.对结果集解析麻烦,sql 变化导致解析代码变化,且解析前需要遍历,如果能将数据库记录封装成 pojo 对象解析比较方便。
  • 解决:Mybatis 自动将 sql 执行结果映射至 java 对象,通过 statement 中的 resultType 定义输出结果的
    类型。

四、Mybatis的参数深入

1、parameterType 配置参数说明

使用标签的 parameterType 属性来设定。该属性的取值可以是基本类型,引用类型(例如:String 类型),还可以是实体类类型(POJO 类)。同时也可以使用实体类的包装类,本章节将介绍如何使用实体类的包装类作为参数传递。

2、注意事项

  • 基 本 类 型 和 String 我 们 可 以 直 接 写 类 型 名 称 , 也 可 以 使 用 包 名 . 类 名 的 方 式 , 例 如 :java.lang.String。
  • 实体类类型,目前我们只能使用全限定类名。
  • 究其原因,是 mybaits 在加载时已经把常用的数据类型注册了别名,从而我们在使用时可以不写包名,
    而我们的是实体类并没有注册别名,所以必须写全限定类名。在今天课程的最后一个章节中将讲解如何注册实体类
    的别名。

3、传递 pojo 包装对象

  • 开发中通过 pojo 传递查询条件 ,查询条件是综合的查询条件,不仅包括用户查询条件还包括其它的查
    询条件(比如将用户购买商品信息也作为查询条件),这时可以使用包装对象传递输入参数。
  • Pojo 类中包含 pojo。
  • 需求:根据用户名查询用户信息,查询条件放到 QueryVo 的 user 属性中。

4、编写 QueryVo

	public class QueryVo implements Serializable {
    
    
		private User user;
		public User getUser() {
    
    
		return user;
		}
		public void setUser(User user) {
    
    
		this.user = user;
		}
	}

5、编写持久层接口

	public interface IUserDao {
    
    
	/**
	* 根据 QueryVo 中的条件查询用户
	* @param vo
	* @return
	*/
	List<User> findByVo(QueryVo vo);
	}

6、持久层接口的映射文件

<!-- 根据用户名称模糊查询,参数变成一个 QueryVo 对象了 -->
	<select id="findByVo" resultType="com.domain.User"
	parameterType="com.itheima.domain.QueryVo">
		select * from user where username like #{user.username};
	</select>

7、测试包装类作为参数

	@Test
	public void testFindByQueryVo() {
    
    
		QueryVo vo = new QueryVo();
		User user = new User();
		user.setUserName("%王%");
		vo.setUser(user);
		List<User> users = userDao.findByVo(vo);
		for(User u : users) {
    
    
		System.out.println(u);
		}
	}

五、 Mybatis 的输出结果封装

1、resultType 配置结果类型

  • resultType 属性可以指定结果集的类型,它支持基本类型和实体类类型。
  • 我们在前面的 CRUD 案例中已经对此属性进行过应用了。
    需要注意的是,它和 parameterType 一样,如果注册过类型别名的,可以直接使用别名。没有注册过的必须
  • 使用全限定类名。例如:我们的实体类此时必须是全限定类名(今天最后一个章节会讲解如何配置实体类的别名)
  • 同时,当是实体类名称是,还有一个要求,实体类中的属性名称必须和查询语句中的列名保持一致,否则无法
    实现封装

2、基本类型示例

Dao接口

	/**
	* 查询总记录条数
	* @return
	*/
	int findTotal();

映射配置

	<!-- 配置查询所有操作 -->
	<select id="findAll" resultType="com.domain.User">
		select * from user
	</select>

特殊示例主要是要求实体类属性和数据库表的列名必须保持一致。

3、resultMap 结果类型

  • resultMap 标签可以建立查询的列名和实体类的属性名称不一致时建立对应关系。从而实现封装。
  • 在 select 标签中使用 resultMap 属性指定引用即可。同时 resultMap 可以实现将查询结果映射为复杂类
    型的 pojo,比如在查询结果映射对象中包括 pojo 和 list 实现一对一查询和一对多查询。

定义 resultMap

	<!-- 建立 User 实体和数据库表的对应关系
	type 属性:指定实体类的全限定类名
	id 属性:给定一个唯一标识,是给查询 select 标签引用用的。
	-->
	<resultMap type="com.domain.User" id="userMap">
		<id column="id" property="userId"/>
		<result column="username" property="userName"/>
		<result column="sex" property="userSex"/>
		<result column="address" property="userAddress"/>
		<result column="birthday" property="userBirthday"/>
	</resultMap>

<!-- * id 标签:用于指定主键字段
     * result 标签:用于指定非主键字段
	 * column 属性:用于指定数据库列名
	 * property 属性:用于指定实体类属性名称
-->

映射配置

	<!-- 配置查询所有操作 -->
	<select id="findAll" resultMap="userMap">
		select * from user
	</select>

测试结果

	@Test
	public void testFindAll() {
    
    
		List<User> users = userDao.findAll();
		for(User user : users) {
    
    
		System.out.println(user);
		}
	}

猜你喜欢

转载自blog.csdn.net/weixin_44676935/article/details/108179156