MyBatis中的接口代理机制及其使用
MyBatis 中的接口代理类机制,MyBatis 框架中使用了动态代理的设计模式,让我们可以不用写,对应XxxMapper.java 接口的实现类,而是通过动态代理的方式,让MyBatis 自动为我们生成对应实现了该 XxxMapper.java接口的实现类,这个动态代理实现的类,我们可以直接使用。
核心代码:
// 获取到 SqlSessionFactoryBuilder SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); // 获取到SqlSessionFactory 对象 // SQlsessionFactory对象,一个SqlSessionFactory对应一个 environment, 一个environment通常是一个数据库 SqlSessionFactory sessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"), "mybatis"); // 获取到 SalSession 会话,一次会话一个 SqlSession sqlSession = sessionFactory.openSession(); Car car = new Car(null, "999", "奥迪", 3.0, "2000-10-10", "新能源"); // 面向接口编程,获取接口的代理对象,也就是接口的实现类,实现类该接口中的方法 // 需要注意的是参数的 Xxxmapper.class 和 返回值是保持一致的。 XxxMapper mapper = sqlSession.getMapper(XxxMapper.class); mapper.xxx(); // 执行的是该XxxMapper接口中的方法
// 获取到 SalSession 会话,一次会话一个 SqlSession sqlSession = sessionFactory.openSession(); Car car = new Car(null, "999", "奥迪", 3.0, "2000-10-10", "新能源"); // 面向接口编程,获取接口的代理对象,也就是接口的实现类,实现类该接口中的方法 // 需要注意的是参数的 Xxxmapper.class 和 返回值是保持一致的。 XxxMapper mapper = sqlSession.getMapper(XxxMapper.class); mapper.xxx(); // 执行的是该XxxMapper接口中的方法
使用以上代码的前提是:XxxMapper.xml 文件中的 namespace
必须和 dao(mapper)
接口的全限定名称一致,id
必须和 dao(mapper)
接口中方法名一致。
比如:我们这里的是:
CarMappe.xml
XxxMapper.xml 文件中的 namespace
必须和 dao(mapper)
接口的全限定名称一致
对应的接口上的方法名,id 必须
和 dao(mapper)
接口中方法名一致。
使用的是 POJO 属性类赋值的话,#{} 的括号中的值,必须是 POJO类当中的属性名,比如这里我们用的是 Car ,则#{}括号中的值,则必须是 Car 的属性名。同时 #{} 括号中一定要有值(就算只有一个参数,也要有值(随便写都要有值),才行,不然编译无法通过)
实操
下面我们使用 MyBatis 的接口代理机制,对数据库进行CRUD,(增删改查)的操作。
1.准备工作
数据表结构的设计,数据表名为:t_car
t_car 表中的数据信息:
在pom.xml
文件当中配置相关的依赖的 jar 包如下:
4.0.0com.rainbowseamybatis-005-crud-blog1.0-SNAPSHOT1717org.mybatismybatis3.5.10mysqlmysql-connector-java8.0.30junitjunit4.13.2testch.qos.logbacklogback-classic1.2.11
配置 logback 的配置文件,用于打印显示,我们的日志信息,方便我们查看我们的运行过程,效果。
%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
配置 MyBatis 的核心配置文件,
对照 t_car 创建的ORM 映射的 Car 类
注意:在MyBatis 当中对应的ORM ,一般在框架里对应的 Bean实体类,一定要实现该 set 和 get 方法以及无参数构造方法,无法框架无法使用反射机制,进行操作 。
建议用包装类,这样可以防止 Null的问题,因为(简单类型 int num = null ,是不可以赋值为 null)的编译无法通过
package com.rainbowsea.mybatis.pojo; public class Car { // 数据库表当中的字段应该和pojo类的属性一一对应 // 建议使用包装类,这样可以防止null的问题 private Long id; private String carNum; private String brand; private Double guidePrice; private String produceTime; private String 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; } @Override public String toString() { return "Car{" + "id=" + id + ", carNum='" + carNum + ''' + ", brand='" + brand + ''' + ", guidePrice=" + guidePrice + ", produceTime='" + produceTime + ''' + ", catType='" + 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 catType) { this.carType = catType; } }
对应操作实现CRUD(增删改查)的接口(这里是:CarMapper接口),在MyBtis 当中 ,关于 CRUD(增删改查)操作的接口/实现类,都是 mapper
结尾的作为持久层,而在 MVC的三层架构中,则是以 dao
为后缀作为CRUD(增删改查)操作的接口/实现类。
package com.rainbowsea.mybatis.mapper; import com.rainbowsea.mybatis.pojo.Car; import java.util.List; public interface CarMapper { /** * 新增 Car * @param car * @return */ int insert(Car car); /** * 根据id 删除 Car * @param id * @return */ int deleteById(Long id); /** * 修改汽车信息 * @param car * @return */ int update(Car car); /** * 根据id查询汽车信息 * @param id * @return */ Car selectById(Long id); /** * 获取所有的汽车信息 * @return */ List selectAll(); }
2.insert 增加操作
对应 CarMapper 接口中的 insert( ) 抽象方法。
public interface CarMapper { /** * 新增 Car * @param car * @return */ int insert(Car car); }
对应 CarMapper.xml SQL语句映射文件,上编写 insert 插入的 SQL语句。
insert into t_car values(null,#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})
Java当中编程运行程序:
注意:因为是对数据库进行了修改,所以需要 commit() 提交给数据库,以及 close() 关闭资源
package com.rainbowsea.mybatis.test; import com.rainbowsea.mybatis.mapper.CarMapper; import com.rainbowsea.mybatis.pojo.Car; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Test; import java.io.IOException; public class CarMapperTest { @Test public void testInsert() throws IOException { // 获取到 SqlSessionFactoryBuilder SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); // 获取到SqlSessionFactory 对象 // SQlsessionFactory对象,一个SqlSessionFactory对应一个 environment, 一个environment通常是一个数据库 SqlSessionFactory sessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"), "mybatis"); // 获取到 SalSession 会话,一次会话一个 SqlSession sqlSession = sessionFactory.openSession(); Car car = new Car(null, "999", "奥迪", 3.0, "2000-10-10", "新能源"); // 面向接口编程,获取接口的代理对象,也就是接口的实现类,实现类该接口中的方法 CarMapper mapper = sqlSession.getMapper(CarMapper.class); int count = mapper.insert(car); System.out.println(count); sqlSession.commit(); sqlSession.close(); } }
3.delete 删除操作
根据 id 删除一条记录,删除id为 124的一条记录。
对应 CarMapper 接口中的 deleteById( Long id) 抽象方法。
public interface CarMapper { /** * 新增 Car * @param car * @return */ int insert(Car car); /** * 根据id 删除 Car * @param id * @return */ int deleteById(Long id); }
对应 CarMapper.xml SQL语句映射文件,上编写 delete 删除的 SQL语句。
使用以上代码的前提是:XxxMapper.xml 文件中的 namespace
必须和 dao(mapper)
接口的全限定名称一致,id
必须和 dao(mapper)
接口中方法名一致。
delete from t_car where id=#{id}
Java当中编程运行程序:
注意:因为是对数据库进行了修改,所以需要 commit() 提交给数据库,以及 close() 关闭资源
删除id为 124的一条记录。
import org.junit.Test; import java.io.IOException; public class CarMapperTest { @Test public void testDeleteById() throws IOException { // 获取到 SqlSessionFactoryBuilder SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); // 获取到SqlSessionFactory 对象 // SQlsessionFactory对象,一个SqlSessionFactory对应一个 environment, 一个environment通常是一个数据库 SqlSessionFactory sessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"), "mybatis"); // 获取到 SalSession 会话,一次会话一个 SqlSession sqlSession = sessionFactory.openSession(); // 面向接口编程,获取接口的代理对象,也就是接口的实现类,实现类该接口中的方法 CarMapper mapper = sqlSession.getMapper(CarMapper.class); // 删除id为 124的一条记录。 int count = mapper.deleteById(124L); sqlSession.commit(); // 提交给数据库 sqlSession.close(); // 关闭资源 System.out.println(count); } }
4.update 修改操作
根据 id 修改记录信息。
将 id 为 128的 brand 改为小米su7, guide_price 改为 21.00 , 时间改为 2024-03-28
对应 CarMapper 接口中的 update( ) 抽象方法。
package com.rainbowsea.mybatis.mapper; import com.rainbowsea.mybatis.pojo.Car; import java.util.List; public interface CarMapper { /** * 修改汽车信息 * @param car * @return */ int update(Car car); }
对应 CarMapper.xml SQL语句映射文件,上编写 update 修改的 SQL语句。
使用以上代码的前提是:XxxMapper.xml 文件中的 namespace
必须和 dao(mapper)
接口的全限定名称一致,id
必须和 dao(mapper)
接口中方法名一致。
update t_car set car_num=#{carNum}, brand=#{brand}, guide_price=#{guidePrice}, produce_time=#{produceTime}, car_type=#{carType} where id = #{id}
Java当中编程运行程序:
注意:因为是对数据库进行了修改,所以需要 commit() 提交给数据库,以及 close() 关闭资源
将 id 为 128的 brand 改为小米su7, guide_price 改为 21.00 , 时间改为 2024-03-28
import com.rainbowsea.mybatis.mapper.CarMapper; import com.rainbowsea.mybatis.pojo.Car; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Test; import java.io.IOException; public class CarMapperTest { @Test public void testUpdate() throws IOException { // 获取到 SqlSessionFactoryBuilder SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); // 获取到SqlSessionFactory 对象 // SQlsessionFactory对象,一个SqlSessionFactory对应一个 environment, 一个environment通常是一个数据库 SqlSessionFactory sessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"), "mybatis"); // 获取到 SalSession 会话,一次会话一个 SqlSession sqlSession = sessionFactory.openSession(); Car car = new Car(128L, "999", "小米su7", 21.0, "2022-03-28", "新能源"); // 面向接口编程,获取接口的代理对象,也就是接口的实现类,实现类该接口中的方法 CarMapper mapper = sqlSession.getMapper(CarMapper.class); int count = mapper.update(car); sqlSession.commit(); // 提交给数据库 sqlSession.close(); } }
5.select 查询一条记录操作
根据 id 查询一条记录。
查询 id 为 130 的一条记录。
对应 CarMapper 接口中的 selectById ( Long id) 抽象方法。
package com.rainbowsea.mybatis.mapper; import com.rainbowsea.mybatis.pojo.Car; import java.util.List; public interface CarMapper { /** * 根据id查询汽车信息 * @param id * @return */ Car selectById(Long id); }
对应 CarMapper.xml SQL语句映射文件,上编写 select 查询 的 SQL语句。
使用以上代码的前提是:XxxMapper.xml 文件中的 namespace
必须和 dao(mapper)
接口的全限定名称一致,id
必须和 dao(mapper)
接口中方法名一致。
需要注意的是:查询是会返回结果集的,所以我们需要在 查询标签当中,通过 resultType 属性指定返回的类型(如果没有用别名机制的话,要用全限定类名(带包名的)) 。
同时由于我们的数据表的字段的命名方式是下划线 ,部分数据表的字段名与我们设置的 ORM 映射的POJO类的属性名不一致,需要将他们二者的名字保持一致,所以我们需要使用 AS 定义别名,不然无法将对应数据表中的值,赋值到 对应的 POJO的类当中(这里是 Car 类当中)
select id, car_num as carNum, brand, guide_price as guidePrice, produce_time as produceTime, car_type as carType from t_car where id = #{id}
Java当中编程运行程序:
注意:因为我们仅仅是查询数据表中的信息,不涉及到对数据表的修改,删除操作,所以无需提交数据库commit,只要 close() 关闭资源就可以了
查询 id 为 130 的一条记录。
import com.rainbowsea.mybatis.mapper.CarMapper; import com.rainbowsea.mybatis.pojo.Car; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Test; import java.io.IOException; public class CarMapperTest { @Test public void testSelectById() throws IOException { // 获取到 SqlSessionFactoryBuilder SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); // 获取到SqlSessionFactory 对象 // SQlsessionFactory对象,一个SqlSessionFactory对应一个 environment, 一个environment通常是一个数据库 SqlSessionFactory sessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"), "mybatis"); // 获取到 SalSession 会话,一次会话一个 SqlSession sqlSession = sessionFactory.openSession(); // 面向接口编程,获取接口的代理对象,也就是接口的实现类,实现类该接口中的方法 CarMapper mapper = sqlSession.getMapper(CarMapper.class); Car car = mapper.selectById(130L); System.out.println(car); sqlSession.close(); } }
6.select 查询多条记录操作
查询t_car 数据表中的所有信息。
对应 CarMapper 接口中的 selectAll( Long id) 抽象方法。返回的是一个List 集合
package com.rainbowsea.mybatis.mapper; import com.rainbowsea.mybatis.pojo.Car; import java.util.List; public interface CarMapper { /** * 获取所有的汽车信息 * @return */ List selectAll(); }
对应 CarMapper.xml SQL语句映射文件,上编写 select 查询 的 SQL语句。
使用以上代码的前提是:XxxMapper.xml 文件中的 namespace
必须和 dao(mapper)
接口的全限定名称一致,id
必须和 dao(mapper)
接口中方法名一致。
需要注意的是:查询是会返回结果集的,所以我们需要在 查询标签当中,通过 resultType 属性指定返回的类型(如果没有用别名机制的话,要用全限定类名(带包名的)) 。
同时由于我们的数据表的字段的命名方式是下划线 ,部分数据表的字段名与我们设置的 ORM 映射的POJO类的属性名不一致,需要将他们二者的名字保持一致,所以我们需要使用 AS 定义别名,不然无法将对应数据表中的值,赋值到 对应的 POJO的类当中(这里是 Car 类当中)
select id, car_num as carNum, brand, guide_price as guidePrice, produce_time as produceTime, car_type as carType from t_car
Java当中编程运行程序:
注意:因为我们仅仅是查询数据表中的信息,不涉及到对数据表的修改,删除操作,所以无需提交数据库commit,只要 close() 关闭资源就可以了
查询 t_car 数据表中的所有记录。
import com.rainbowsea.mybatis.mapper.CarMapper; import com.rainbowsea.mybatis.pojo.Car; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Test; import java.io.IOException; import java.util.List; public class CarMapperTest { @Test public void testSelectAll() throws IOException { // 获取到 SqlSessionFactoryBuilder SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); // 获取到SqlSessionFactory 对象 // SQlsessionFactory对象,一个SqlSessionFactory对应一个 environment, 一个environment通常是一个数据库 SqlSessionFactory sessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"), "mybatis"); // 获取到 SalSession 会话,一次会话一个 SqlSession sqlSession = sessionFactory.openSession(); // 面向接口编程,获取接口的代理对象,也就是接口的实现类,实现类该接口中的方法 CarMapper mapper = sqlSession.getMapper(CarMapper.class); List cars = mapper.selectAll(); cars.forEach(car -> { System.out.println(car); }); sqlSession.close(); } }
总结
// 获取到 SalSession 会话,一次会话一个 SqlSession sqlSession = sessionFactory.openSession(); Car car = new Car(null, "999", "奥迪", 3.0, "2000-10-10", "新能源"); // 面向接口编程,获取接口的代理对象,也就是接口的实现类,实现类该接口中的方法 // 需要注意的是参数的 Xxxmapper.class 和 返回值是保持一致的。 XxxMapper mapper = sqlSession.getMapper(XxxMapper.class); mapper.xxx(); // 执行的是该XxxMapper接口中的方法
使用以上代码的前提是:XxxMapper.xml 文件中的 namespace
必须和 dao(mapper)
接口的全限定名称一致,id
必须和 dao(mapper)
接口中方法名一致。
注意:因为是对数据库进行了修改,删除,改动了,所以需要 commit() 提交给数据库,以及 close() 关闭资源
需要注意的是:查询是会返回结果集的,所以我们需要在 查询标签当中,通过 resultType 属性指定返回的类型(如果没有用别名机制的话,要用全限定类名(带包名的)) 。
同时由于我们的数据表的字段的命名方式是下划线 ,部分数据表的字段名与我们设置的 ORM 映射的POJO类的属性名不一致,需要将他们二者的名字保持一致,所以我们需要使用 AS 定义别名,不然无法将对应数据表中的值,赋值到 对应的 POJO的类当中(这里是 Car 类当中)
注意:因为我们仅仅是查询数据表中的信息,不涉及到对数据表的修改,删除操作,所以无需提交数据库commit,只要 close() 关闭资源就可以了。
如果只有一个参数需要传的话,#{} 括号中的值,可以随便写(#{}括号的值不能空着,不然不编译无法通过),但最好见名知意。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持IT俱乐部。