Mybatis基本运行流程
总结JDBC的问题:
1、数据库连接创建、释放频繁造成系统资源浪费,从而影响系统性能。如果使用数据库连接池可解决此问题。
2、Sql语句在代码中硬编码,造成代码不易维护,实际应用中sql变化的可能较大,sql变动需要改变java代码。
3、使用preparedStatement向占有位符号传参数存在硬编码,因为sql语句的where条件不一定,可能多也可能少,修改sql还要修改代码,系统不易维护。
4、对结果集解析存在硬编码(查询列名),sql变化导致解析代码变化,系统不易维护,如果能将数据库记录封装成pojo对象解析比较方便。
1.Mybatis介绍
MyBatis是一个支持普通SQL查询,存储过程和高级映射的优秀持久层框架。MyBatis消除了几乎所有的JDBC代码和参数的手工设置以及对结果集的检索封装。MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO映射成数据库中的记录
mybatis通过xml或注解进行配置,将java对象与sql语句中的参数自动映射生成最终执行的sql语句,并将sql语句执行结果自动映射成java对象,返回给业务层(service)应用。
Mybatis的整体架构:
2.Mybatis的生命周期
正确理解SqlSessionFactory、SqlSessionFactoryBuilder、SqlSession和Mapper的生命周期对于优化Mybatis尤为重要,这样可以使Mybatis高效正确完成;同为重要时Mybatis的生命周期对于理解Myabtis缓存的配置也尤为重要,我这里只做简单的文字介绍(其实也好理解):
(1)SqlSessionFactoryBuilder:作用就是创建一个构建器,一旦创建了SqlSessionFactory,它的任务就算完成了,可以回收。
(2)SqlSessionFactory:作用是创建SqlSession,而SqlSession相当于JDBC的一个Connection对象,每次应用程序需要访问数据库,我们就要通过SqlSessionFactory创建一个SqlSession,所以SqlSessionFactory在整Mybatis整个生命周期中(每个数据库对应一个SqlSessionFactory,是单例产生的)。
(3)SqlSession:生命周期是存在于请求数据库处理事务的过程中,是一个线程不安全的对象(在多线程的情况下,需要特别注意),即存活于一个应用的请求和申请,可以执行多条SQL保证事务的一致性。
(4)Mapper:是一个接口,它的作用是发送SQL(其中的方法相当于JDBC中的Statement),返回我们需要的结果,或者发送SQL修改数据库表,所以它存活于一个SqlSession内,是一个方法级别的东西。当SqlSession销毁的时候,Mapper也会销毁。
为什么SqlSessionFactory是单例的:
SqlSessionFactory是创建SqlSession的工厂,但是创建过程中需要反复加载全局配置文件,这一点是十分耗时的,为了优化项目,最好通过单例模式来管理它,使它只能创建一个对象,配置文件加载一次就可以了。(在mybatis整合spring之后,最好的方式是把sqlsessionfactory交给spring来做单例管理)
3.Mybatis的配置
3.1单独使用Mybatis的配置
1.按照上面的步骤首先定义一个User类
User.java
public class User {
private Integer id;
private String username;
private Integer[] ids;
private List<Integer> idlist;
public Integer[] getIds() {
return ids;
}
public void setIds(Integer[] ids) {
this.ids = ids;
}
public List<Integer> getIdlist() {
return idlist;
}
public void setIdlist(List<Integer> idlist) {
this.idlist = idlist;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
@Override
public String toString() {
return "User [id=" + id + ", username=" + username + "]";
}
}
2.接着定义一个Mapper接口:定义了一些方法来操作数据库,方法中具体sql语句的实现在相应的xml文件中。
UserMapper.interface
public interface UserMapper {
/*
* 遵循四个原则:
* 1.方法名和UseMapper.xml中<mapper>标签中sql标签的id属性值相同
* 2.方法的返回值和<mapper>标签中标签的resultType属性值相同
* 3.方法的参数类型和<mapper>标签中标签的parameterType属性值相同
* 4.xml中<mapper>标签的namespace属性值和此接口的全类名相同
*/
User findUserById(Integer id);
}
3.接着定义Mapper的映射文件UserMapper.xml,此文件的作用就是映射方法实现具体的SQL语句。
<mapper namespace="Mapper.UserMapper">
<!-- 设置一个sql片段 可以SQL语句中在引用-->
<sql id="selector">
select * from user
</sql>
<select id(方法名)="findUserById" parameterType(方法的参数类型)="Integer" resultType(方法的返回类型)="pojo.User">
<include refid="selector"/>
where id = #{id}
</select>
</mapper>
4.定义了UserMapper和UserMapper.xml之后那么程序如何才能寻找到呢,通过Mybatis的配置文件。
sqlMapConfig.xml
<configuration>
<!-- 指定加载配置文件 -->
<properties resource="jdbc.properties"/>
<!-- 别名设置 -->
<typeAliases>
<package name="pojo"/>
</typeAliases>
<!-- 和Spring整合后这个标签就没用了 -->
<environments default="development">
<environment id="development">
<!-- 使用jdbc事务管理 -->
<transactionManager type="JDBC" />
<!-- 数据库连接池 -->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}" />
<property name="url"
value="jdbc:mysql://localhost:3306/student" />
<property name="username" value="root" />
<property name="password" value="123456" />
</dataSource>
</environment>
</environments>
<!-- 引入User的xml映射文件 我的UserMapper.java
和UserMapper.xml都在Mapper包下-->
<mappers>
<package name="Mapper"/>
<!-- package这种方式需要接口名和xml文件名相同,直接导入包下所有配置文件
-->
</mappers>
</configuration>
5.配置好了配置文件创建一个demo实例来加载配置文件并创建sqlSessionFactory
UserDemo.java
public class UserDemo {
@Test
//通过id查询值
public void fun() throws IOException{
// 1.加载配置文件
InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
// 2.创建SqlSessionFactory
SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(in);
// 3.创建sqlSession
SqlSession sqlSession=sqlSessionFactory.openSession();
// 4.sqlSeesion动态创建UserMapper实现类
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 5.实现类调用接口的方法,方法和映射文件一一对应
User user=userMapper.findUserById(1);
System.out.println(user);
}
3.2Mybatis整合Spring
单独使用Mybatis时我们需要通过加载配置文件来创建SqlsessionFactory,得到sqlsession,然后通过sqlsession来创建mapper接口的代理类,最后通过代理类来执行sql语句。
整合Spring之后,通过在applicationContext.xml中注入两个类来实现上面所有的操作。原理还是一样,只不过spring替我们简化了操作,并且dateSource的操作放在了spring的配置文件中。
applicationContext.xml
context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 将连接池注入容器 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<property name="maxActive" value="10" />
<property name="maxIdle" value="5" />
</bean>
<!--配置工厂 -->
<bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!-- 核心配置文件的位置 -->
<property name="configLocation" value="classpath:sqlMapConfig.xml"/>
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 扫描包下的所有接口 -->
<property name="basePackage" value="mapper"/>
<!-- 这里会自动注入mapper接口
<bean name="userMapper" class="mapper.UserMapper"/>
-->
</bean>
</beans>
这里的SqlSessionFactoryBean在Spring中最后创建的并不是Bean本身而是sqlSessionFactory
sqlMapConfig.xml文件只需设置一个别名(或者也不设置)
<configuration>
<!-- 设置别名 -->
<typeAliases>
<!-- 2. 指定扫描包,会把包内所有的类都设置别名,别名的名称就是类名,大小写不敏感 -->
<package name="pojo" />
</typeAliases>
</configuration>
UserDemo.java
@Test
public void testMapper() throws Exception {
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
UserMapper mapper = ac.getBean(UserMapper.class);
User user = mapper.selectUserById(1);
System.out.println(user);
}
这里的getBean会创建UserMapper的实现类