1.MyBatis 介绍
MyBatis是一种持久层框架,和Hibernate等一样是作为操作数据库底层的处理方案。持久层的作用可以把实体对象转换为SQL语句,或者使用自定义的SQL语句去操作数据库,然后把返回的数据再映射为实体对象返回给调用者。
在传统的 JDBC 中,我们除了需要自己提供 SQL 外,还必须操作 Connection、Statment、ResultSet,不仅如此,为了访问不同的表,不同字段的数据,我们需要些很多雷同模板化的代码,闲的繁琐又枯燥。
而使用了 MyBatis 之后,只需要提供 SQL 语句就好了,其余的诸如:建立连接、操作 Statment、ResultSet,处理 JDBC 相关异常等等都可以交给 MyBatis 去处理,我们的关注点于是可以就此集中在 SQL 语句上,关注在增删改查这些操作层面上。
1.1. SpringMVC下MyBatis的集成
1.1.1. Spring工程搭建
1.1.1.1. 创建Maven工程
创建一个 Maven工程,工程结构目录如下:
1.1.1.2. 配置pom文件
将需要的包引入到工程里面,以下是需要的jar包配置。
<dependencies>
<!--junit测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!--日志相关-->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
<version>1.7.21</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
<scope>test</scope>
</dependency>
<!--mybatis的包-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.2.8</version>
</dependency>
<!--mybatis和spring整合相关的包-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.2.2</version>
</dependency>
<!--spring相关-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.1.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<exclusions>
<!-- 排除掉 Commons Logging 相关的包 -->
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
<version>4.1.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>4.1.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.1.3.RELEASE</version>
</dependency>
<!--MySQL驱动包-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
</dependency>
<!--数据连接池相关包-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.12</version>
</dependency>
</dependencies>
1.1.1.3. 增加相关的配置文件
如果是开发完整的web工程,需要编写相应的配置文件,我们只是为了简单的使用测试使用一下MyBatis相关的功能,所以只配置这一个相关的即可。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 引入配置文件 -->
<context:property-placeholder location="classpath:config/db.properties"/>
<!-- 配置数据源 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<property name="driverClassName" value="${jdbc.driver}" />
<property name="maxActive" value="10" />
<property name="minIdle" value="5" />
<property name="logAbandoned" value="true" />
</bean>
<!-- 配置sqlsessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="configLocation" value="classpath:mybatis/SqlMapConfig.xml"></property>
</bean>
<!-- 配置扫描包,加载mapper代理对象 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.zzlh.mp.mapper"></property>
</bean>
<!--如果不是用自动扫描,则需要为每一个mapper代理对象手动配置
<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="com.zzlh.mp.mapper.UserMapper" />
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
-->
</beans>
注:1、在配置sqlsessionFactory中关于configLocation是配置MyBatis自己相关的一些配置的,目前使用不到,可以不用配置。
2、配置扫描包路径必须和项目中真实路径保持一致
这里看到引入了一个数据库相关的配置文件,所以编写一个db.properties,编写数据库连接相关的内容
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/mp?characterEncoding=utf-8
jdbc.username=root
jdbc.password=root
还需要一个Mybatis自己相关的配置文件SqlMapConfig,,里面配置了日志和缓存相关的内容。
<?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="logImpl" value="LOG4J"/>
<!-- 二级缓存开启 -->
<setting name="cacheEnabled" value="true"/>
<setting name="lazyLoadingEnabled" value="false"/>
<setting name="aggressiveLazyLoading" value="true"/>
</settings>
</configuration>
此时的工程目录结构如下:
接下来编写实体和Mybatis的mapper代理,由于我们只是使用junit进行单元测试的方式进行mybatis的学习,就不编写controller相关的代码了。
1.1.1.4. 编写相关的Java代码
在src/java下面新建com.zzlh.mp.entity包,里面存放的是各种实体类对应数据库中的每一张表。我们以user表为例创建一个User.java文件。
public class User {
//对应数据库中的表字段
private Long id;
private String name;
private Integer age;
private String email;
public User() {
}
//方便创建测试用例重写了实例化方法
public User(Long id, String name, Integer age, String email) {
this.id = id;
this.name = name;
this.age = age;
this.email = email;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
//方便打印输出,重写了toString方法
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
", email='" + email + '\'' +
'}';
}
在src/java下面新建com.zzlh.mp.mapper包,里面存放的是MyBatis的代理接口和对应的xml文件。
首先UserMapper.java,是一个接口类,里面定义了各种接口方法。
package com.zzlh.mp.mapper;
import com.zzlh.mp.entity.User;
import java.util.List;
import java.util.Map;
public interface UserMapper {
int insertUser(User user);
int deleteUserById(int userId);
int updateUser(User user);
User getUserById(int userId);
User selectOne(User user);
List<User> listUserByMap(Map<String,Object> paramMap);
List<User> listUser(User user);
List<User> selectAll();
}
然后是UserMapper.xml,里面是根据Mybatis语法规则编写的sql语句。
<?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">
<mapper namespace="com.zzlh.mp.mapper.UserMapper">
<resultMap id="UserMap" type="com.zzlh.mp.entity.User">
<id property="id" column="id" />
<result property="name" column="name" />
<result property="age" column="age" />
<result property="email" column="email" />
</resultMap>
<insert id="insertUser" parameterType="com.zzlh.mp.entity.User">
insert into user (id,name,age,email) value (#{id,jdbcType=DECIMAL},#{name,jdbcType=VARCHAR},#{age,jdbcType=DECIMAL},#{email,jdbcType=VARCHAR})
</insert>
<delete id="deleteUserById" parameterType="int">
delete from user where id = #{userId,jdbcType=DECIMAL}
</delete>
<update id="updateUser" parameterType="com.zzlh.mp.entity.User">
update user set name=#{name,jdbcType=VARCHAR},age=#{age,jdbcType=DECIMAL},email=#{email,jdbcType=VARCHAR} where id=#{id,jdbcType=DECIMAL}
</update>
<select id="getUserById" parameterType="int" resultType="com.zzlh.mp.entity.User">
select * from user where id=#{userId,jdbcType=DECIMAL}
</select>
<select id="selectOne" parameterType="com.zzlh.mp.entity.User" resultType="com.zzlh.mp.entity.User">
select * from user where id=#{userId,jdbcType=DECIMAL}and name=#{name,jdbcType=VARCHAR} and age=#{age,jdbcType=DECIMAL} and email=#{email,jdbcType=VARCHAR}
</select>
<select id="listUserByMap" parameterType="hashmap" resultType="com.zzlh.mp.entity.User">
select * from user where id=#{userId,jdbcType=DECIMAL}and name=#{name,jdbcType=VARCHAR} and age=#{age,jdbcType=DECIMAL} and email=#{email,jdbcType=VARCHAR}
</select>
<select id="listUser" parameterType="com.zzlh.mp.entity.User" resultType="com.zzlh.mp.entity.User">
select * from user where id=#{userId,jdbcType=DECIMAL}and name=#{name,jdbcType=VARCHAR} and age=#{age,jdbcType=DECIMAL} and email=#{email,jdbcType=VARCHAR}
</select>
<select id="selectAll" resultType="com.zzlh.mp.entity.User">
select * from user
</select>
</mapper>
我们可以看到xml文件中每一个标签通过id和java文件的接口方法一一对应。里面就是我们写的SQL语句,然后参数通过MyBatis的表达式来进行标识。这时候的工程目录结构如下:
1.1.1.5. 编写测试类
在test/java下面创建com.zzlh.mp包,在里面首先添加一个JUnit4ClassRunner.java,这个类用来加载log4j.properties文件。
public class JUnit4ClassRunner extends SpringJUnit4ClassRunner {
static {
try {
Log4jConfigurer.initLogging("config/log4j.properties");
} catch (FileNotFoundException ex) {
System.err.println("Cannot Initialize log4j");
}
}
public JUnit4ClassRunner(Class<?> clazz) throws InitializationError {
super(clazz);
}
}
然后编写一个测试类,SampleTest.java来测试功能。
@RunWith(JUnit4ClassRunner.class)//自定义加载过log4j配置文件的类
@ContextConfiguration(locations = {"classpath*:spring.xml"})
public class SampleTest {
@Autowired
private UserMapper userMapper;
@Test
public void testSelect() {
List<User> userList = userMapper.selectAll();
userList.forEach(System.out::println);
}
}
工程目录如下:
运行测试方法testSelect(),在控制台输出的日志可以看到以下异常:
org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.zzlh.mp.mapper.UserMapper.selectAll
at org.apache.ibatis.binding.MapperMethod$SqlCommand.<init>(MapperMethod.java:189)
at org.apache.ibatis.binding.MapperMethod.<init>(MapperMethod.java:43)
at org.apache.ibatis.binding.MapperProxy.cachedMapperMethod(MapperProxy.java:58)
at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:51)
at com.sun.proxy.$Proxy14.selectAll(Unknown Source)
at com.zzlh.mp.SampleTest.testSelect(SampleTest.java:33)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:73)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:82)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:73)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:217)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:83)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:68)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:163)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
这是编译的时候,src/java下面的*maaper.xml的文件没有被编译,所以我们需要在pom文件里面配置将它编译。
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
再次运行测试类,查看日志,执行成功,打印出来sql语句和输出结果。
com.zzlh.mp.mapper.UserMapper.selectAll - ==> Preparing: select * from user
com.zzlh.mp.mapper.UserMapper.selectAll - ==> Parameters:
com.zzlh.mp.mapper.UserMapper.selectAll - <== Total: 7
org.mybatis.spring.SqlSessionUtils - Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2692b61e]
org.springframework.jdbc.datasource.DataSourceUtils - Returning JDBC Connection to DataSource
User{id=1, name='Jone', age=18, email='test1@baomidou.com'}
User{id=2, name='Jack', age=20, email='test2@baomidou.com'}
User{id=3, name='Tom', age=28, email='test3@baomidou.com'}
User{id=4, name='Sandy', age=21, email='test4@baomidou.com'}
User{id=5, name='Billie', age=24, email='test5@baomidou.com'}
User{id=6, name='张三', age=34, email='22@qq.com'}
User{id=7, name='李四', age=33, email='ls@qq.com'}
注:1、控制台输出的sql语句是没有经过整合的,就是语句和参数是分离的,如果需要排查问题,需要自己整合sql语句,如果参数过多可能比较麻烦,针对IDEA有MyBatis的插件MyBatis Log Plugin可以将sql自动整合。
2、mapper.java和mapper.xml放在一起不用配置,但是一般把配置类的文件放在java目录里面比较不推荐,一般是将xml文件放在resources里面然后通过spring配置文件指定即可。所以我们可以改为。
首先将pom的build配置取消,然后在spring.xml文件里面sqlSessionFactory配置上mapper.xml的路径。
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="configLocation" value="classpath:mybatis/SqlMapConfig.xml"></property>
<!--增加mapper.xml的路径 -->
<property name="mapperLocations" value="classpath:mapper/*.xml"></property>
</bean>
此时目录结构如下:
再次运行测试类,运行成功。
1.1.1.6. 小结
1、MyBatis这种持久化技术和jdbc、hibernate对比的特点是
- 首先是sql和java代码分离,
好处是让sql分离维护,临时修改不需要重新编译。
- 支持定制化 SQL、存储过程以及高级映射。
- 可以使用简单的 XML 或注解来配置和映射原生类型、接口和 Java 的 POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
2、MyBatis的缺点
- mapper.xml里面的sql片段不容易维护。当一个mapper里面的方法越来越多,sql片段就会越凌乱,不方便查找,在IDEA之前的Eclipse等编辑器里面要想找到一个对应的sql片段效率是很低的。
举个例子 cust-mapper.xml - 可定制化的有点是灵活,缺点就是不容易维护和二次开发,比如数据库字段更新了,要重新生成po和sql代码片段,如果是我们对po文件增加过属性,那么重新生成的po文件就不能直接使用要么用对比工具排查,要么使用包装类继承原生po。不管怎么处理都不方便维护和管理。
- 每一个对应的mapper都要写大量的基础增删改查的方法和语句。
共同学习,写下你的评论
评论加载中...
作者其他优质文章