文章目录
参考文章:什么是MyBatis
学习之前,跟你们说点事情,有助于你能快速看完文章
很多同学对classpath
这个东西不明白,在MyBatis中的classpath
表示:main/java
、main/resources
、第三方jar包的根目录
一、先应用再学习,代码示例
1. 第一个MyBatis程序
配置MyBatis
引入MyBatis的Jar包
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.21</version>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
</dependency>
<!--配置全局-->
<!--在build中配置resource,来防止我们资源到处失败的问题-->
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
MyBatis配置文件
<?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>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="com.mysql.cj.jdbc.Driver"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/mybatis/dao/UserMapper.xml"/>
</mappers>
</configuration>
MyBatis工具类
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
//使用Mybatis第一步: 获取sqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
//既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。
//sqlSession 完全包含了面向数据库执行SQL命令所需的所有方法
public static SqlSession getSqlsession() {
// SqlSession sqlSession = sqlSessionFactory.openSession();
return sqlSessionFactory.openSession();
}
}
数据库搭建
CREATE DATABASE `mybatis`;
USE `mybatis`;
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(20) NOT NULL,
`name` varchar(20) DEFAULT NULL,
`pwd` varchar(40) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into `user`(`id`,`name`,`pwd`) values (1,'小明','123456'),(2,'张三','abcdef'),(3,'李四','987654');
编程篇
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private int id;
private String name;
private String pwd;
}
public interface UserMapper {
//查询全部用户
List<User> getUserList();
}
编写xxxMapper接口的配置文件xxxMapper.xml
<?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.mybatis.dao.UserMapper">
<select id="getUserList" resultType="com.mybatis.pojo.User">
select * from mybatis.user
</select>
</mapper>
测试接口
@Test
public void test(){
SqlSession sqlSession = MybatisUtils.getSqlseesion();
UserDao userdao = sqlSession.getMapper(UserDao.class);
List<User> userList = userdao.getUserList();
for(User user : userList){
System.out.println(user);
}
sqlSession.close();
}
2. MyBatis整合Spring
配置MyBatis+Spring篇
<!--spring核心ioc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<!--做spring事务用到的-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<!--mybatis依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.1</version>
</dependency>
<!--mybatis和spring集成的依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.1</version>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.9</version>
</dependency>
<!--阿里公司的数据库连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.12</version>
</dependency>
<build>
<!--目的是把src/main/java目录中的xml文件包含到输出结果中。输出到classes目录中-->
<resources>
<resource>
<directory>src/main/java</directory><!--所在的目录-->
<includes><!--包括目录下的.properties,.xml 文件都会扫描到-->
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
<!--指定jdk的版本-->
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
MyBatis核心配置文件
<?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:控制mybatis全局行为-->
<settings>
<!--设置mybatis输出日志-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<!--设置别名-->
<typeAliases>
<!--
name:实体类所在的包名
表示com.bjpowernode.domain包中的列名就是别名
你可以使用Student表示com.bjpowenrode.domain.Student
包下的所有类的类名则为别名
-->
<package name="com.SpringStudy.domain"/>
</typeAliases>
<!--
一个mapper标签指定一个文件的位置。
从类路径开始的路径信息
target/classes(类路径)
-->
<mappers>
<!--
name:是包名, 这个包中的所有mapper.xml一次都能加载
-->
<package name="com.SpringStudy.dao"/>
</mappers>
</configuration>
Spring配置文件spring-dao.xml
<?xml version="1.0" encoding="GBK"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
<!--DataSource:使用Spring的数据源替换Mybatis的配置 c3p0 dbcp druid
我们这里使用Spring提供的JDBC:-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<!--sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<!--关联mybatis配置文件-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:com/kuang/mapper/*.xml"/>
</bean>
<!--SqlSessionTemplate:就是我们使用的sqlSession-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<!--只能使用构造器注入sqlSessionFactory,因为它没有set方法-->
<constructor-arg index="0" ref="sqlSessionFactory" />
</bean>
</beans>
数据库篇
数据库搭建
CREATE DATABASE `mybatis`;
USE `mybatis`;
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(20) NOT NULL,
`name` varchar(20) DEFAULT NULL,
`pwd` varchar(40) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into `user`(`id`,`name`,`pwd`) values (1,'小明','123456'),(2,'张三','abcdef'),(3,'李四','987654');
编程篇
实体类User
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private int id; //id
private String name; //姓名
private String pwd; //密码
}
实体类接口Mapper
public interface UserMapper {
//查询全部用户
List<User> getUserList();
}
xxxMapper接口的配置文件xxxMapper.xml
<?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.mybatis.dao.UserMapper">
<select id="getUserList" resultType="com.mybatis.pojo.User">
select * from mybatis.user
</select>
</mapper>
UserMapper接口的UserMapperImpl 实现类,私有化sqlSessionTemplate
public class UserMapperImpl implements UserMapper {
//我们的所有操作,都使用sqlSession来执行,在原来,现在都使用SqlsessionTemplate
private SqlSessionTemplate sqlSession;
public void setSqlSession(SqlSessionTemplate sqlSession) {
this.sqlSession = sqlSession;
}
public List<User> selectUser() {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
return mapper.selectUser();
}
}
将自己写的实现类,注入到Spring配置文件spring-dao.xml
<bean id="userMapper" class="com.kuang.mapper.UserMapperImpl">
<property name="sqlSession" ref="sqlSession"/>
</bean>
测试篇
@Test
public void test () throws IOException {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-dao.xml");
UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
for (User user : userMapper.selectUser()) {
System.out.println(user);
}
}
3. SpringBoot整合MyBatis
配置篇
SpringBoot+MyBatis配置文件
<!--Springboot需要的依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--Mybatis项目需要的依赖-->
<!--mysqlq驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.12</version>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.4</version>
</dependency>
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!--Springboot-Mybatis整合需要的依赖-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.1</version>
</dependency>
<build>
<!--目的是把src/main/java目录中的xml文件包含到输出结果中。输出到classes目录中-->
<resources>
<resource>
<directory>src/main/java</directory><!--所在的目录-->
<includes><!--包括目录下的.properties,.xml 文件都会扫描到-->
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
<!--指定jdk的版本-->
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
配置核心配置类application.yml:放在Resource文件夹下,SpringBoot会自己扫描到
spring:
datasource:
name: pro_man_sys_db
url: jdbc:mysql://localhost:3306/pro_man_sys_db?serverTimezone=UTC
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
resources:
static-locations: classpath:/static,classpath:/resources,file:/root/uploadFiles
mybatis:
type-aliases-package: com.shiliuzi.model
mapper-locations: classpath:mapper/*.xml
数据库篇
CREATE DATABASE `mybatis`;
USE `mybatis`;
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(20) NOT NULL,
`name` varchar(20) DEFAULT NULL,
`pwd` varchar(40) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into `user`(`id`,`name`,`pwd`) values (1,'小明','123456'),(2,'张三','abcdef'),(3,'李四','987654');
编程篇
创建实体类User
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private int id; //id
private String name; //姓名
private String pwd; //密码
}
创建实体类接口Mapper
@Mapper
public interface UserMapper {
//查询全部用户
List<User> getUserList();
}
配置Mapper.xml文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--configuration core file-->
<mapper namespace="com.guo.mapper.UserMapper">
<select id="queryUserList" resultType="User">
</select>
</mapper>
二、ORM(对象关系映射)
- 简单:将MySQL的一张表映射成一个Java类
- 易懂:MySQL数据库就被ORM转换为java程序员可以读懂的java类
- 易用:ORM将sql查询全部封装成了编程语言中的函数
三、MyBatis整体流程,各组件的作用域和生命周期
执行流程
MyBatis组件
1. 配置解析
- 可以添加配置信息的地方:mybatis-config.xml、Mapper.xml、MapperInterface注解信息。
- Mybatis初始化过程中,会加载配置信息,并保存到Configuration对象中。
- Configuration对象可以创建SqlSessionFactory对象,之后即可创建SqlSession对象执行数据库操作
2. SQL解析
- SQL解析会根据运行时用户传入的实参,解析动态SQL中的标签,形成SQL模板,然后处理SQL模板中的占位符,用运行时的实参填充占位符,得到数据库真正可执行的SQL语句。
3. SQL执行
- Executor:调用事务管理模块实现事务的相关控制,同时管理一级缓存和二级缓存
- StatementHandler:SQL语句开始执行,依赖于ParameterHandler的实参绑定,然后执行SQL语句,从数据库中拿到ResultSet
- ParameterHandler:进行SQL模板的实参绑定
- ResultSetHandler:将ResultSet映射成Java对象返回给调用方
四、说说MyBatis-config.xml核心配置文件吧
主要说说接口和对应xml映射文件的位置问题,可以通过mapper映射标签解决
Mybatis中接口和对应的mapper文件不一定要放在同一个包下,如果放在一起的目的是为了Mybatis进行自动扫描,并且要注意此时Java接口的名称和mapper文件的名称要相同,否则会报异常,由于此时Mybatis会自动解析对应的接口和相应的配置文件,所以就不需要配置mapper文件的位置了。
如果接口和mapper文件不在同一个包下,就不能进行自动扫描解析了,需要对接口和文件分别进行配置。
<!--方式一:使用相对于类路径的资源引用-->
<mappers>
<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
</mappers>
<!--方式一:使用映射器接口实现类的完全限定类名 -->
<mappers>
<mapper class="org.mybatis.builder.AuthorMapper"/>
</mappers>
<!--方式三:将包内的映射器接口实现全部注册为映射器 -->
<mappers>
<package name="org.mybatis.builder"/>
</mappers>
五、讲讲xxxMapper.xml文件中的标签吧
如果童鞋们将上述的三个项目搭建认真看完并且总结了,我相信MyBatis在使用层面就没什么问题了,这里就再补充一下xxxMapper.xml文件的标签问题就好了。
1. CRUD标签
<!--对象中的属性,可以直接取出来-->
<select id="getUserById" parameterType="int" resultType="com.kuang.pojo.User">
select * from mybatis.user where id = #{id}
</select>
<insert id="addUser" parameterType="com.kuang.pojo.User">
insert into mybatis.user (id, name, pwd) values (#{id},#{name},#{pwd});
</insert>
<update id="updateUser" parameterType="com.kuang.pojo.User">
update mybatis.user set name=#{name},pwd=#{pwd} where id = #{id} ;
</update>
<delete id="deleteUser" parameterType="int">
delete from mybatis.user where id = #{id};
</delete>
2. parameterType传入参数
parameterType = 实体类
如果 User 类型的参数对象传递到了语句中,会查找 id、username 和 password 属性,然后将它们的值传入预处理语句的参数中。对传递语句参数来说,这种方式真是干脆利落。
<insert id="insertUser" parameterType="User">
insert into users (id, username, password) values (#{id}, #{username}, #{password})
</insert>
parameterType = map
<!--对象中的属性,可以直接取出来 传递map的key-->
<insert id="addUser" parameterType="map">
insert into mybatis.user (id, pwd) values (#{userid},#{passWord});
</insert>
@Test
public void addUser2(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Map<String, Object> map = new HashMap<String, Object>();
map.put("userid",5);
map.put("passWord","2222333");
mapper.addUser2(map);
sqlSession.close();
}
六、MyBatis剩下的问题,在这里总结一下
1. MyBatis的事务问题:CRUD需要提交事务!可以通过手动提交和自动提交完成:
- 手动提交:sqlSession.commit();
- 自动提交:sqlSessionFactory.openSession(true);
2. 属性名和字段名不一致:通过ReslutMap
但是MyBatis-plus底层会去识别数据库字段,然后遇到下划线就会转化为下一个字母的大写,也就是驼峰命名转化。
<resultMap id="WaterSupplyProduction" type="com.ruoyi.domain.monitor.WaterSupplyProduction">
<!-- 定义主键 ,非常重要。如果是多个字段,则定义多个id -->
<!-- property:主键在pojo中的属性名 -->
<!-- column:主键在数据库中的列名 -->
<id property="id" column="id"/>
<!-- 定义普通属性 -->
<result property="areaName" column="area_name"/>
<result property="factoryId" column="factory_id"/>
<result property="monitorRecordId" column="monitor_record_id"/>
<result property="riverLevel" column="river_level"/>
<result property="riverTurbidity" column="river_turbidity"/>
<result property="riverFlowRate" column="river_flow_rate"/>
<result property="handledTurbidity" column="handled_turbidity"/>
<result property="poolLevel" column="pool_level"/>
<result property="factoryWaterTurbidity" column="factory_water_turbidity"/>
<result property="factoryWaterPH" column="factory_water_pH"/>
<result property="factoryWaterChlorine" column="factory_water_chlorine"/>
<result property="factoryWaterRate" column="factory_water_rate"/>
<result property="createTime" column="create_time"/>
</resultMap>
<select id="selectAll" resultMap="WaterSupplyProduction">
select * from water_supply_production
</select>
3. 使用注解开发项目:实际上就是将xxxMapper.xml与xxxMapper整合在一起
如果把xxxMapper.xml与xxxMapper整合在一起,那就应该把Mapper映射器位置改成xxxMapper接口位置
<mappers>
<mapper class="com.itheima.dao.UserMapper"></mapper>
</mappers>
public interface UserMapper {
@Select("select * from user")
List<User> getUsers();
//方法存在多个参数,所有的参数前面必须加上@param("id")
@Select("select * from user where id = #{id}")
User getUserById(@Param("id") int id);
@Insert("insert into user(id,name,pwd) values(#{id},#{name},#{paswword})")
int addUser(User user);
@Update("update user set name=#{name},pwd=#{password} where id=#{id}")
int updateUser(User user);
@Delete("delete from user where id=#{id}")
int deleteUser(@Param("id") int id);
}
4. MyBatis的二级缓存
在MyBatis-config.xml核心配置文件开启二级缓存支持
<!-- 配置二级缓存 -->
<settings>
<!-- 开启二级缓存的支持 -->
<setting name="cacheEnabled" value="true"/>
</settings>
在持久层接口中使用注解配置二级缓存
@CacheNamespace(blocking = true)
public interface UserMapper{
}
七、Hibernate与MyBatis的区别
1. 以Hibernate实例,实现ORM思想
参考文章:Hibernate入门这一篇就够了
参考文章:ORM框架原理与应用——实现ORM框架入门实例
配置环境和配置文件,我就不做了,这里主要是展示一下ORM思想的实现,具体配置在参考文章
- User实体类
public class User {
private int id;
private String username;
private String password;
private String cellphone;
//各种setter和getter
}
- 编写对象映射User.hbm.xml
<!--在domain包下-->
<hibernate-mapping package="zhongfucheng.domain">
<!--类名为User,表名也为User-->
<class name="User" table="user">
<!--主键映射,属性名为id,列名也为id-->
<id name="id" column="id">
<!--根据底层数据库主键自动增长-->
<generator class="native"/>
</id>
<!--非主键映射,属性和列名一一对应-->
<property name="username" column="username"/>
<property name="cellphone" column="cellphone"/>
<property name="password" column="password"/>
</class>
</hibernate-mapping>
- 编写主配置文件hibernate.cfg.xml
<hibernate-configuration>
<!-- 通常,一个session-factory节点代表一个数据库 -->
<session-factory>
<!-- 1. 数据库连接配置 -->
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql:///zhongfucheng</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">root</property>
<!--
数据库方法配置, hibernate在运行的时候,会根据不同的方言生成符合当前数据库语法的sql
-->
<property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
<!-- 2. 其他相关配置 -->
<!-- 2.1 显示hibernate在运行时候执行的sql语句 -->
<property name="hibernate.show_sql">true</property>
<!-- 2.2 格式化sql -->
<property name="hibernate.format_sql">true</property>
<!-- 2.3 自动建表 -->
<property name="hibernate.hbm2ddl.auto">create</property>
<!--3. 加载所有映射-->
<mapping resource="zhongfucheng/domain/User.hbm.xml"/>
</session-factory>
</hibernate-configuration>
- 测试
public class App {
public static void main(String[] args) {
//创建对象
User user = new User();
user.setPassword("123");
user.setCellphone("122222");
user.setUsername("nihao");
//获取加载配置管理类
Configuration configuration = new Configuration();
//不给参数就默认加载hibernate.cfg.xml文件,
configuration.configure();
//创建Session工厂对象
SessionFactory factory = configuration.buildSessionFactory();
//得到Session对象
Session session = factory.openSession();
//使用Hibernate操作数据库,都要开启事务,得到事务对象
Transaction transaction = session.getTransaction();
//开启事务
transaction.begin();
//把对象添加到数据库中
session.save(user);
//提交事务
transaction.commit();
//关闭Session
session.close();
}
}
2. Mybatis与hibernate的对比
1. 开发速度的对比
就开发速度而言,Hibernate的真正掌握要比Mybatis来得难些。Mybatis框架相对简单很容易上手,但也相对简陋些。个人觉得要用好Mybatis还是首先要先理解好Hibernate。
比起两者的开发速度,不仅仅要考虑到两者的特性及性能,更要根据项目需求去考虑究竟哪一个更适合项目开发,比如:一个项目中用到的复杂查询基本没有,就是简单的增删改查,这样选择hibernate效率就很快了,因为基本的sql语句已经被封装好了,根本不需要你去写sql语句,这就节省了大量的时间,但是对于一个大型项目,复杂语句较多,这样再去选择hibernate就不是一个太好的选择,选择mybatis就会加快许多,而且语句的管理也比较方便。
2. 开发工作量的对比
Hibernate和MyBatis都有相应的代码生成工具。可以生成简单基本的DAO层方法。针对高级查询,Mybatis需要手动编写SQL语句,以及ResultMap。而Hibernate有良好的映射机制,开发者无需关心SQL的生成与结果映射,可以更专注于业务流程。
3. sql优化方面
Hibernate的查询会将表中的所有字段查询出来,这一点会有性能消耗。Hibernate也可以自己写SQL来指定需要查询的字段,但这样就破坏了Hibernate开发的简洁性。而Mybatis的SQL是手动编写的,所以可以按需求指定查询的字段。
Hibernate HQL语句的调优需要将SQL打印出来,而Hibernate的SQL被很多人嫌弃因为太丑了。MyBatis的SQL是自己手动写的所以调整方便。但Hibernate具有自己的日志统计。Mybatis本身不带日志统计,使用Log4j进行日志记录。
4. 对象管理的对比
Hibernate 是完整的对象/关系映射解决方案,它提供了对象状态管理(state management)的功能,使开发者不再需要理会底层数据库系统的细节。也就是说,相对于常见的 JDBC/SQL 持久层方案中需要管理 SQL 语句,Hibernate采用了更自然的面向对象的视角来持久化 Java 应用中的数据。
换句话说,使用 Hibernate 的开发者应该总是关注对象的状态(state),不必考虑 SQL 语句的执行。这部分细节已经由 Hibernate 掌管妥当,只有开发者在进行系统性能调优的时候才需要进行了解。而MyBatis在这一块没有文档说明,用户需要对对象自己进行详细的管理
八、Mybatis插件
MyBatis 允许你在映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:
- Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
- ParameterHandler (getParameterObject, setParameters)
- ResultSetHandler (handleResultSets, handleOutputParameters)
- StatementHandler (prepare, parameterize, batch, update, query)
这些类中方法的细节可以通过查看每个方法的签名来发现,或者直接查看 MyBatis 发行包中的源代码。 如果你想做的不仅仅是监控方法的调用,那么你最好相当了解要重写的方法的行为。 因为在试图修改或重写已有方法的行为时,很可能会破坏 MyBatis 的核心模块。 这些都是更底层的类和方法,所以使用插件的时候要特别当心。
通过 MyBatis 提供的强大机制,使用插件是非常简单的,只需实现 Interceptor 接口,并指定想要拦截的方法签名即可。
主要需要实现三个方法
intercept:在此实现自己的拦截逻辑,可从Invocation参数中拿到执行方法的对象,方法,方法参数,从而实现各种业务逻辑, 如下代码所示,从invocation中获取的statementHandler对象即为被代理对象,基于该对象,我们获取到了执行的原始SQL语句,以及prepare方法上的分页参数,并更改SQL语句为新的分页语句,最后调用invocation.proceed()返回结果。
plugin:生成代理对象;
setProperties:设置一些属性变量;
1. 定义插件
// ExamplePlugin.java
/**
* @Intercepts 注解标记这是一个拦截器,其中可以指定多个@Signature
* @Signature 指定该拦截器拦截的是四大对象中的哪个方法
* type:拦截器的四大对象的类型
* method:拦截器的方法,方法名
* args:入参的类型,可以是多个,根据方法的参数指定,以此来区分方法的重载
*/
@Intercepts({@Signature(
type= Executor.class, //指定组件
method = "update", //指定组件中的方法
args = {MappedStatement.class,Object.class})})
public class ExamplePlugin implements Interceptor {
public Object intercept(Invocation invocation) throws Throwable {
System.out.println("拦截器执行:"+invocation.getTarget());
//目标对象
Object target = invocation.getTarget();
//获取目标对象中所有属性的值,因为ParameterHandler使用的是DefaultParameterHandler,因此里面的所有的属性都封装在其中
MetaObject metaObject = SystemMetaObject.forObject(target);
//使用xxx.xxx.xx的方式可以层层获取属性值,这里获取的是mappedStatement中的id值
String value = (String) metaObject.getValue("mappedStatement.id");
//如果是指定的查询方法
if ("cn.cb.demo.dao.UserMapper.selectByUserId".equals(value)){
//设置参数的值是admin_1,即是设置id=admin_1,因为这里只有一个参数,可以这么设置,如果有多个需要需要循环
metaObject.setValue("parameterObject", "admin_1");
}
//执行目标方法
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
//如果没有特殊定制,直接使用Plugin这个工具类返回一个代理对象即可
return Plugin.wrap(target, this);
}
//设置一些属性,不重要。
@Override
public void setProperties(Properties properties) {
}
}
2. 插件注入
这插件最终还是从IOC容器中获取的Interceptor[]这个Bean,因此我们只需要在配置类中注入这个Bean即可
/**
* @Configuration:这个注解标注该类是一个配置类
*/
@Configuration
public class MybatisConfig{
/**
* @Bean : 该注解用于向容器中注入一个Bean
* 注入Interceptor[]这个Bean
* @return
*/
@Bean
public Interceptor[] interceptors(){
//创建ParameterInterceptor这个插件
ParameterInterceptor parameterInterceptor = new ParameterInterceptor();
//放入数组返回
return new Interceptor[]{parameterInterceptor};
}
}
3. 测试
@Test
void contextLoads() {
//传入的是1222
UserInfo userInfo = userMapper.selectByUserId("1222");
System.out.println(userInfo);
}
4. 结果
测试代码传入的是1222,由于插件改变了入参,因此查询出来的应该是admin_1这个人。
5. 小结
简单的说,mybatis插件就是对ParameterHandler、ResultSetHandler、StatementHandler、Executor这四个接口上的方法进行拦截,利用JDK动态代理机制,为这些接口的实现类创建代理对象,在执行方法时,先去执行代理对象的方法,从而执行自己编写的拦截逻辑
九、MyBatis中的$和#有什么区别
select ${arg0} from USER where userName = #{userName} and userPassword = #{userPassword};
使用#设置参数时
:MyBatis会创建预编译的SQL语句,然后在执行SQL时MyBatis会为预编译SQL中的占位符(?)赋值。预编译的SQL语句执行效率高,并且可以防止注入攻击。
使用$设置参数时
:MyBatis只是创建普通的SQL语句,然后在执行SQL语句时MyBatis将参数直接拼入到SQL里。这种方式在效率、安全性上均不如前者,但是可以解决一些特殊情况下的问题。例如,在一些动态表格(根据不同的条件产生不同的动态列)中,我们要传递SQL的列名,根据某些列进行排序,或者传递列名给SQL都是比较常见的场景,这就无法使用预编译的方式了。
十、MyBatis的缓存机制
1. 一级缓存
一级缓存基于sqlSession默认开启,不同的SqlSession之间的缓存数据区域是互相不影响的。当在同一个sqlSession中执行两次相同的sql语句时,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次查询时会从缓存中获取数据,不再去底层数据库查询,从而提高查询效率。
需要注意的是,如果SqlSession执行了DML操作(增删改),并且提交到数据库,MyBatis则会清空SqlSession中的一级缓存,这样做的目的是为了保证缓存中存储的是最新的信息,避免出现脏读现象。
2. 二级缓存
二级缓存的作用域是mapper的同一个namespace。不同的sqlSession两次执行相同的namespace下的sql语句,且向sql中传递的参数也相同,即最终执行相同的sql语句,则第一次执行完毕会将数据库中查询的数据写到缓存,第二次查询会从缓存中获取数据,不再去底层数据库查询,从而提高效率。
3. 设置二级缓存
在MyBatis 的全局配置settings 中有一个参数cacheEnabled,这个参数是二级缓存的全局开关,默认值是true ,初始状态为启用状态。
MyBatis 的二级缓存是和命名空间绑定的,即二级缓存需要配置在Mapper.xml 映射文件中。在保证二级缓存的全局配置开启的情况下,给Mapper.xml 开启二级缓存只需要在Mapper. xml 中添加如下代码:
<cache />