在上一篇文章中我们粗浅的了解了什么是MyBatis,MyBatis的特点。并动手实现了一个MyBatis程序,算是简单的入了个门
上篇文章
在本篇文章中我们会更多的将内容偏向于实践
MyBatis CRUD 操作对应的是,mapper.xml配置文件中的一些标签
在上篇文章中有简单的使用select标签,现在我们再认识一下这个标签
将上篇文章中的select标签拿过来,可以看到标签中有两个属性可以进行配置
id 属性用于绑定mapper 标签中 namespace 属性指定接口的对应方法resultType 则和它的名字一样,指定是返回结果的类型 唯一需要注意的是,如果类型为一个具体的类 则需要写全路径名。除了这两个属性以外,我们还会用到的属性有parameterType
同它的名字一样,意思是参数类型 为了更好的理解它,我们可以写一个查询示例首先在mapper接口中再定义一个查询方法
这个方法就需要参数了,就涉及到了参数类型
//通过id查询用户 User getUserById(int id);修改mapper.xml中的select标签
对应的select标签中 parameterType属性就需要配置参数类型为int类型
sql 接收传递的参数时,使用 #{} 来接收
<select id="getUserById" parameterType="int" resultType="com.molu.pojo.User"> select * from mybatis.user where id = #{id} </select>使用junit快速完成测试
@Test public void testGetUser(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); User user = mapper.getUserById(2); System.out.println(user); sqlSession.close(); } }测试结果:
insert 标签对应增删改查中的增,它用来插入数据
该标签的使用比较简单,唯一需要注意的是,增删改都需要提交事务。不然就是白写
我们照样通过一个示例来理解 insert 标签的使用
// 在mapper接口中简单写一个增加用户的方法 public interface UserMapper { // 增加一个用户 int addUser(User user); }同样的在mapper.xml中进行改动
id 仍然绑定 接口中方法parameterType 写惨数类型,由于参数类型是一个类,所以我们必须写全限定名。在insert 标签体中写sql 仍然使用 #{} 的方式来接收传入的参数 <insert id="addUser" parameterType="com.molu.pojo.User"> insert into mybatis.user (id,name,pwd) values (#{id},#{name},#{pwd}); </insert>使用junit快速测试
@Test public void addUserTest(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); int num = mapper.addUser(new User(4, "林", "270")); // 一定要记得提交事务,不然就白写了 // 使用 sqlSession.commit(); 方法来提交事务 sqlSession.commit(); sqlSession.close(); } }测试结果:略
如果你代码写得没问题、数据库没问题、事务也没忘, 那应该是能够添加成功的。
update 标签对应增删改中改,用于修改表中的数据
同上,快速的写一个示例来进行说明
// 在mapper接口中 写一个修改用户的方法 public interface UserMapper { // 修改指定用户信息 int updateUser(User user); }在mapper.xml中对应的写一个update标签
相信到这里应该都没什么疑惑,不再过多赘述
<update id="updateUser" parameterType="com.molu.pojo.User"> update mybatis.user set name = #{name} where id = #{id}; </update>使用junit快速进行测试
@Test public void updateUserTest(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); int num = mapper.updateUser(new User(4, "章", "")); sqlSession.commit(); sqlSession.close(); } }测试结果: 成功将 id为 4的用户name设置为"章",且对密码不进行改动
delete 标签对应增删改中删,用于删除表中的数据
同上
public interface UserMapper { // 在mapper接口中简单的写一个查询方法 List<User> getUserList(); // 通过id查询用户 User getUserById(int id); // 增加一个用户 int addUser(User user); // 修改指定用户信息 int updateUser(User user); // 删除指定用户 int deleteUser (int id); }在mapper.xml中对应的写一个delete标签
<delete id="deleteUser" parameterType="int"> delete from mybatis.user where id = #{id}; </delete>快速测试
@Test public void deleteUserTest(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); mapper.deleteUser(4); sqlSession.commit(); sqlSession.close(); }测试结果: 成功删除 id为 4 的用户数据
MyBatis中实现增删改查还是十分容易的,唯一需要注意的就是返回值类型参数类型这些。(增删改提交事务也不要漏)
具体的步骤大致就是
在mapper接口中编写方法在mapper.xml中写对应的标签和sql在测试类中调用接口中的方法,并传入指定的参数还是比较省事的相对JDBC来说
在MyBatis中我们能配置的东西很多很多
MyBatis官网也就配置给出了长长的一篇参考文档,可以结合该文档和 本篇博客对MyBatis的配置进行了解
当然由于篇幅问题,这些配置我们不可能都讲到,也不可能都讲的很深入,望谅解
MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中, 现实情况下有多种理由需要这么做。例如,开发、测试和生产环境需要有不同的配置;或者想在具有相同 Schema 的多个生产数据库中使用相同的 SQL 映射。还有许多类似的使用场景。
不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。
<!--环境,可以定义多套环境--> <environments default="development"> <!--使用的环境--> <environment id="development"> <!--默认使用JDBC事务管理--> <transactionManager type="JDBC"/> <!--默认使用连接池--> <dataSource type="POOLED"> <!--用户登录信息--> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> <!-- 新增的环境,如果要切换至该环境可以在environments default=""处将其设置为 environments default="test" --> <environment id="test"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> </environments>事务管理器(transactionManager)
在 MyBatis 中有两种类型的事务管理器(也就是 type="[JDBC | MANAGED]"):
JDBC – 这个配置直接使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。
MANAGED – 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。 默认情况下它会关闭连接。然而一些容器并不希望连接被关闭,因此需要将 closeConnection 属性设置为 false 来阻止默认的关闭行为。例如:
<transactionManager type="MANAGED"> <property name="closeConnection" value="false"/> </transactionManager>数据源(dataSource)
dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源。
大多数 MyBatis 应用程序会按示例中的例子来配置数据源。虽然数据源配置是可选的,但如果要启用延迟加载特性,就必须配置数据源。有三种内建的数据源类型(也就是 type="[UNPOOLED|POOLED|JNDI]")
这些属性可以在外部进行配置,并可以进行动态替换。你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置。
简单的来说就是,你可以在property标签中将你的属性写死,也可以引入其他配置文件中配置好的属性。
以下是两种不同的情况示例
写死在property标签种
<property name="driver" value="com.mysql.cj.jdbc.Driver"/> <!--在property标签种无法直接使用 & 要写成 & 来转义。--> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?serverTimezone=GMT%2B8&useSSL=true&useUnicode=true&characterEncoding=UTF-8"/> <property name="username" value="root"/> <property name="password" value="*******"/>引入其他配置文件种的属性(在上一篇文章中已经写过了,直接引用)
首先在resources目录下创建一个db.properties文件在db.properties文件中,写你的配置信息 driver=com.mysql.jdbc.Driver # 在properties文件中可以直接使用 & 而不需要转义 url=jdbc:mysql://localhost:3306/数据库名?serverTimezone=GMT%2B8&useSSL=true&useUnicode=true&characterEncoding=UTF-8 username=用户名 password=密码 # 数据库登录信息, 时区设置为东八区,如果你是使用的8版本驱动就需要设置这个时区(serverTimezone=GMT%2B8)将db.properties引入到mybatis配置文件中
<!--<propertie>标签它必须置于<configuration>标签内的最顶部,否则会出现异常--> <properties resource="db.properties"/>在mybatis配置文件中引入db.properties中配置好的登录信息
在property标签value值处,使用$符号和一对大括号,引用db.properties文件中的配置信息,如下。默认从官网复制下来就是这样,不需要改动
<dataSource type="POOLED"> <!--用户登录信息--> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource>类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。
在我们的mapper.xml中,每一次返回值类型、参数类型是一个对象时我们都需要写全限定名,这样看起来自然不是很舒服,而我们就可以通过别名来对其进行优化‘
首先在核心配置文件中加入typeAliases标签,标签中再写一个typeAlias标签
在typeAlias标签中将属性 type 设置为你想要取别名的类(全限定名),alias属性设置为它的别名
<typeAliases> <typeAlias type="com.molu.pojo.User" alias="User"/> </typeAliases>在原先的mapper.xml配置文件中,存在大量的全限定名。而使用别名后,这些全限定名都可以使用它的别名User来代替,能稍微的偷一点懒。
也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:
<typeAliases> <package name="com.molu.pojo"/> </typeAliases>该包中的 Java Bean,在没有注解的情况下,默认会使用 Bean 的首字母小写的非限定类名来作为它的别名。
这样的方式有一定的局限性,但使用起来也更(方便)偷懒一些。
<update id="updateUser" parameterType="user"> update mybatis.user set name = #{name} where id = #{id}; </update>别名还是需要掌握的,关于别名更多的可以移步MyBatis官网。
这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。 下表描述了设置中各项设置的含义、默认值等。
略
篇幅过长,可以移步官网,官网写得十分详细
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vieRnRjM-1603357725531)(C:\Users\86186\AppData\Roaming\Typora\typora-user-images\image-20201022153654415.png)]
在上一篇文章中,写mapper.xml映射文件的时候并没有过多的提到映射器,我们当时图方便直接是将映射文件写到了resources目录下,这样写直接就可以通过映射文件的名字来绑定映射
除了这种比较偷懒的方式,映射文件的绑定 实际上也是可以展开的
第一种方式:resource
如果我们将mapper.xml映射文件放到resources目录下,是可以直接通过映射文件名来进行绑定的,也就是前面我们偷懒一直在用的方式
<!--和MyBatis配置文件放在同一个目录下(resources)--> <mappers> <mapper resource="mapper.xml"/> </mappers>如果不放在resources目录下,我们还可以将他放在mapper包下和mapper接口写在一起。就像这样:
<!-- 使用相对于类路径的资源引用 --> <mappers> <mapper resource="com/molu/mapper/mapper.xml"/> </mappers>我在上一篇文章中也有说到过,如果不放在resources目录下,可能会碰到一个maven资源过滤的问题
可能会找不到你写的映射文件,继而报错。
解决的方式也很简单,将下面这行代码放到你的pom.xml中即可
<build> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>true</filtering> </resource> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>true</filtering> </resource> </resources> </build>第二种方式:class
我们可以使用这种方式来进行映射文件的绑定
需要注意的有两点
映射文件必须和接口在同一个包下映射文件必须和接口同名像这样:
<!-- 使用映射器接口实现类的完全限定类名 --> <mappers> <mapper class="com.molu.mapper.UserMapper"/> </mappers>第三种方式:mapper
我们也可以通过这种方式,将包内的映射器接口实现全部注册为映射器。
<mappers> <package name="com.molu.mapper"/> </mappers>需要注意的点也和上面一样,需要同名同包。否则会报错
还有一种通过url来绑定,那种的话比较麻烦 这里就不写了,感兴趣可以看看官网
还有一些比较复杂三言两语说不清,或者很少用到的配置 这边就不赘述了,感兴趣可以看看官网。官网还是都有提到的
生命周期和作用域,是至关重要的,因为错误的使用会导致非常严重的并发问题。
以下引自MyBatis官方文档,作为本篇文章的补充和收尾
提示 :对象生命周期和依赖注入框架
依赖注入框架可以创建线程安全的、基于事务的 SqlSession 和映射器,并将它们直接注入到你的 bean 中,因此可以直接忽略它们的生命周期。 如果对如何通过依赖注入框架使用 MyBatis 感兴趣,可以研究一下 MyBatis-Spring 或 MyBatis-Guice 两个子项目。
这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。 因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。 你可以重用 SqlSessionFactoryBuilder 来创建多个 SqlSessionFactory 实例,但最好还是不要一直保留着它,以保证所有的 XML 解析资源可以被释放给更重要的事情。
SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。 使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次,多次重建 SqlSessionFactory 被视为一种代码“坏习惯”。因此 SqlSessionFactory 的最佳作用域是应用作用域。 有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。
每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。 绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。 也绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中,比如 Servlet 框架中的 HttpSession。 如果你现在正在使用一种 Web 框架,考虑将 SqlSession 放在一个和 HTTP 请求相似的作用域中。 换句话说,每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它。 这个关闭操作很重要,为了确保每次都能执行关闭操作,你应该把这个关闭操作放到 finally 块中。 下面的示例就是一个确保 SqlSession 关闭的标准模式:
try (SqlSession session = sqlSessionFactory.openSession()) { // 你的应用逻辑代码 }在所有代码中都遵循这种使用模式,可以保证所有数据库资源都能被正确地关闭。
