昨天写了一个 mybatis 的helloWord,虽然能跑起来,但是那种方式还是会存在一些问题。
- 每次进行增删改查,方法里传入的 sql 唯一标识id 就好长一串。
- 再者就是传入的查询入参类型是一个object,也就是什么都可以往里面传,如果传"a",肯定查不出来数据。
那么,mybatis 还提供了另一种更高级的使用方式:接口式编程,这也是后续开发中主要使用的方式。
我们可以使用一个接口来描述一个给定的sql语句,它的参数和返回值。
一、接口与配置文件动态绑定
在测试平台项目下的 dao 层新建一个接口com.pingguo.bloomtest.dao.UserMapper
:
package com.pingguo.bloomtest.dao; import com.pingguo.bloomtest.pojo.User; public interface UserMapper { User getUserById(Integer id); }
定义好了接口,里面有一个方法getUserById
,顾名思义就是通过id查询数据,返回 User 对象。
按照以往,定义好了接口,接下来还需要定义出接口的实现类。
但是现在这个接口就是用来查询出 user 表记录数据并封装成 User 对象返回,而在之前定义好的 sql 映射文件UserMapper.xml
里,<select>
标签里的 sql 也是做这个事情。
<mapper namespace="org.mybatis.example.UserMapper"> <select id="selectUser" resultType="com.pingguo.bloomtest.pojo.User"> select * from user where id = #{id} </select> </mapper>
那么,现在就可以把接口与配置文件进行动态绑定:
namespace
:不要随便写了,对应接口的全类名id
: 对应接口里的方法名
改完之后应该是这样了:
<mapper namespace="com.pingguo.bloomtest.dao.UserMapper"> <select id="getUserById" resultType="com.pingguo.bloomtest.pojo.User"> select * from user where id = #{id} </select> </mapper>
再写一个测试方法查询一下:
@Test void test2() throws IOException { // 根据配置文件,创建一个 SqlSessionFactory 对象 SqlSessionFactory sqlSessionFactory = getSqlSessionFactory(); // 获取 SqlSession 实例,可以执行已经映射的 sql 语句 SqlSession session = sqlSessionFactory.openSession(); // 获取接口的实现类对象 UserMapper userMapper = session.getMapper(UserMapper.class); User user = userMapper.getUserById(4); System.out.println(user); }
getMapper
方法可以获取到这个接口的实现对象。
第一步因为要经常使用,所以抽出去了:
public SqlSessionFactory getSqlSessionFactory() throws IOException { String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); return new SqlSessionFactoryBuilder().build(inputStream); }
执行测试,查询成功。
那么问题来了,这个接口还没写实现类,那么
`UserMapper userMapper = session.getMapper(UserMapper.class);`
获取到的是什么?
加个打印System.out.println(userMapper.getClass());
看下输出。
原来是个代理对象。
所以说,只要把接口和 xml文件进行动态绑定,mybatis 会为接口自动创建一个代理对象,然后由代理对象去执行增删改查方法。
二、关于 SqlSession
SqlSession
代表和数据库的一次会话。用完必须关闭,我上面的事例应该加上session.close();
。
另外,SqlSession
也是非线程安全。
- 线程安全:就是在多线程环境下也不会出现数据不一致,
- 非线程安全: 就有可能出现数据不一致的情况。
所以不能像这样写:
如果这样写,可能会出现A线程 用了 SqlSession,然后关掉了,但是线程B 还在用。所以每次使用都应该去获取新的对象,不要放在成员变量中。