Mybatis与Hibernate区别
- hibernate:它是一个标准的全自动的orm框架,可以自动生成sql语句,比较重量级,学习成本高.对象/关系映射能力强,数据库无关性好
1:优点:高度封装,使用起来不用写sql,开发的时候,会减低开发周期.
2:缺点:sql语句无法优化,学习门槛高,要精通门槛更高。性能和对象模型之间如何权衡
3: 应用场景
oa(办公自动化系统), erp(企业的流程系统)等,还有一些政府项目,总的来说,在用于量不大,并发量小的时候使用. - mybatis:它不完全是一个orm框架, 它是对jdbc的轻量级封装, 学习成本低,比较简单,需要程序员自己编写Sql语句
1:优点:学习成本低, sql语句可以优化, 执行效率高,速度快
2:缺点:编码量较大,会拖慢开发周期
3:应用场景: 互联网项目,比如电商,P2p等。总的来说是用户量较大,并发高的项目.
MyBatis的一级缓存和二级缓存
- Mybatis首先去缓存中查询结果集,如果没有则查询数据库,如果有则从缓存取出返回结果集就不走数据库。Mybatis内部存储缓存使用一个HashMap,key为hashCode+sqlId+Sql语句。value为从查询出来映射生成的java对象
- Mybatis的二级缓存即查询缓存,它的作用域是一个mapper的namespace,即在同一个namespace中查询sql可以从缓存中获取数据。二级缓存是可以跨SqlSession的。
介绍
- MyBatis是一个优秀的持久层框架,它对jdbc的操作数据库的过程进行封装,使开发者只需要关注 SQL 本身,而不需要花费精力去处理例如注册驱动、创建connection、创建statement、手动设置参数、结果集检索等jdbc繁杂的过程代码。
- Mybatis通过xml或注解的方式将要执行的各种statement(statement、preparedStatement、CallableStatement)配置起来,并通过java对象和statement中的sql进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射成java对象并返回。
解释 - SqlMapConfig.xml,此文件作为mybatis的全局配置文件,配置了mybatis的运行环境等信息。
- mapper.xml文件即sql映射文件,文件中配置了操作数据库的sql语句。此文件需要在SqlMapConfig.xml中加载。
- 通过mybatis环境等配置信息构造SqlSessionFactory即会话工厂
- 由会话工厂创建sqlSession即会话,操作数据库需要通过sqlSession进行。
- mybatis底层自定义了Executor执行器接口操作数据库,Executor接口有两个实现,一个是基本执行器、一个是缓存执行器。
- Mapped Statement也是mybatis一个底层封装对象,它包装了mybatis配置信息及sql映射信息等。mapper.xml文件中一个sql对应一个Mapped Statement对象,sql的id即是Mapped statement的id。
- Mapped Statement对sql执行输入参数进行定义,包括HashMap、基本类型、pojo,Executor通过Mapped Statement在执行sql前将输入的java对象映射至sql中,输入参数映射就是jdbc编程中对preparedStatement设置参数。
- Mapped Statement对sql执行输出结果进行定义,包括HashMap、基本类型、pojo,Executor通过Mapped Statement在执行sql后将输出结果映射至java对象中,输出结果映射过程相当于jdbc编程中对结果的解析处理过程。
环境搭建
- SqlMapConfig.xml是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>
<!-- 和spring整合后 environments配置将废除-->
<environments default="development">
<environment id="development">
<!-- 使用jdbc事务管理-->
<transactionManager type="JDBC" />
<!-- 数据库连接池-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8" />
<property name="username" value="root" />
<property name="password" value="123" />
</dataSource>
</environment>
</environments>
</configuration>
- 日志配置
#Global logging configuration
log4j.rootLogger=DEBUG, stdout
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
- 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">
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1MldSmpg-1569727837428)(https://img-blog.csdn.net/20170716094020249?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveWVpd2VpbGFu/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)]
- mybatis框架需要加载映射文件,将Users.xml添加在SqlMapConfig.xml,如下:
<mappers>
<mapper resource="User.xml"/>
</mappers>
细节
- #{}占位符:占位
如果传入的是基本类型,那么#{}中的变量名称可以随意写
如果传入的参数是pojo类型,那么#{}中的变量名称必须是pojo中的属性 ${}
拼接符:字符串原样拼接
如果传入的是基本类型,那么${}
中的变量名必须是value
如果传入的参数是pojo类型,那么${}
中的变量名称必须是pojo中的属性
注意:使用拼接符有可能造成sql注入,在页面输入的时候可以加入校验,不可输入sql关键字,不可输入空格
Dao的开发方式
Mapper动态代理方式
Mapper接口开发方法只需要程序员编写Dao接口,由Mybatis框架根据接口定义创建接口的动态代理对象。
- Mapper接口开发需要遵循以下规范:
Mapper.xml(映射文件)文件中的namespace等于Dao接口的全路径名称
Mapper.xml中sql语句id要等于Mapper接口方法名称
mapper.xml中传入的parameterType的类型等于Mapper接口方法的输入参数类型
mapper.xml中定义的每个sql的resultType的类型等于Mapper接口方法的返回值类型。 - 定义mapper映射文件UserMapper.xml(内容同Users.xml),需要修改namespace的值为 UserMapper接口路径。将UserMapper.xml放在classpath 下mapper目录 下。
<?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="cn.itcast.mybatis.mapper.UserMapper">
<!-- 自定义条件查询用户列表 -->
<select id="findUserByUsername" parameterType="java.lang.String"
resultType="cn.itcast.mybatis.po.User">
select * from user where username like '%${value}%'
</select>
<!-- 添加用户 -->
<insert id="insertUser" parameterType="cn.itcast.mybatis.po.User">
<selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
select LAST_INSERT_ID()
</selectKey>
insert into user(username,birthday,sex,address)
values(#{username},#{birthday},#{sex},#{address})
</insert>
</mapper>
- 测试
Public class UserMapperTest extends TestCase {
private SqlSessionFactory sqlSessionFactory;
protected void setUp() throws Exception {
//mybatis配置文件
String resource = "sqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
//使用SqlSessionFactoryBuilder创建sessionFactory
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
@Test
public void testFindUserByUsername() throws Exception {
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> list = userMapper.findUserByUsername("张");
System.out.println(list.size());
}
Public void testInsertUser() throws Exception {
//获取session
SqlSession session = sqlSessionFactory.openSession();
//获取mapper接口的代理对象
UserMapper userMapper = session.getMapper(UserMapper.class);
//要添加的数据
User user = new User();
user.setUsername("张三");
user.setBirthday(new Date());
user.setSex("1");
user.setAddress("北京市");
//通过mapper接口添加用户
userMapper.insertUser(user);
//提交
session.commit();
//关闭session
session.close();
}
}
SqlMapConfig.xml配置文件
- properties(属性)
- settings(全局配置参数)
- typeAliases(类型别名)
- typeHandlers(类型处理器)
- objectFactory(对象工厂)
- plugins(插件)
- environments(环境集合属性对象)
- environment(环境子属性对象)
- transactionManager(事务管理)
- dataSource(数据源)
- mappers(映射器)
properties
- SqlMapConfig.xml可以引用java属性文件中的配置信息如下:
在classpath下定义db.properties文件,
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8
jdbc.username=root
jdbc.password=root
SqlMapConfig.xml引用如下:
<properties resource="db.properties"/>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
注意: MyBatis 将按照下面的顺序来加载属性:
- 在 properties 元素体内定义的属性首先被读取。
- 然后会读取properties 元素中resource或 url 加载的属性,它会覆盖已读取的同名属性。
typeAliases
- mybatis支持别名:
别名 映射的类型
_byte byte
_long long
_short short
_int int
_integer int
_double double
_float float
_boolean boolean
string String
byte Byte
long Long
short Short
int Integer
integer Integer
double Double
float Float
boolean Boolean
date Date
decimal BigDecimal
bigdecimal BigDecimal
map Map
- 自定义别名:
在SqlMapConfig.xml中配置:
<typeAliases>
<!-- 单个别名定义 -->
<typeAlias alias="user" type="cn.itcast.mybatis.po.User"/>
<!-- 批量别名定义,扫描整个包下的类,别名为类名(首字母大写或小写都可以) -->
<package name="cn.itcast.mybatis.po"/>
<package name="其它包"/>
</typeAliases>
mappers(映射器)
- Mapper配置的几种方法:
<mapper resource=" " />
使用相对于类路径的资源
如:<mapper resource="sqlmap/User.xml" />
<mapper class=" " />
使用mapper接口类路径
如:<mapper class="cn.itcast.mybatis.mapper.UserMapper"/>
注意:此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中。
package name=""/>
注册指定包下的所有mapper接口
如:<package name="cn.itcast.mybatis.mapper"/>
注意:此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中。
输入映射和输出映射
输入参数映射
- Sql语句
SELECT * FROM user where username like '%刘%'
- Mapper文件
<select id="findUserByQueryVo" parameterType="queryvo" resultType="user">
SELECT * FROM user where username like '%${user.username}%'
</select>
- 测试方法
@Test
public void testFindUserByQueryVo() throws Exception {
SqlSession sqlSession = sessionFactory.openSession();
//获得mapper的代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//创建QueryVo对象
QueryVo queryVo = new QueryVo();
//创建user对象
User user = new User();
user.setUsername("刘");
queryVo.setUser(user);
//根据queryvo查询用户
List<User> list = userMapper.findUserByQueryVo(queryVo);
System.out.println(list);
sqlSession.close();
}
>
动态sql
通过mybatis提供的各种标签方法实现动态拼接sql。
- If
<!-- 传递pojo综合查询用户信息 -->
<select id="findUserList" parameterType="user" resultType="user">
select * from user where 1=1
<if test="id!=null">
and id=#{id}
</if>
<if test="username!=null and username!=''">
and username like '%${username}%'
</if>
</select>
注意要做不等于空字符串校验。
- Where
<where />
可以自动处理第一个and。
<select id="findUserList" parameterType="user" resultType="user">
select * from user
<where>
<if test="id!=null and id!=''">
and id=#{id}
</if>
<if test="username!=null and username!=''">
and username like '%${username}%'
</if>
</where>
</select>
- Foreach
传入多个id查询用户信息,用下边两个sql实现:
SELECT * FROM USERS WHERE username LIKE '%张%' AND (id =10 OR id =89 OR id=16)
在povo中定义list属性ids存储多个用户id,并添加getter/setter方法
- mapper.xml
<if test="ids!=null and ids.size>0">
<foreach collection="ids" open=" and id in(" close=")" item="id" separator="," >
#{id}
</foreach>
</if>
- Sql片段
可将重复的sql提取出来,使用时用include引用即可,最终达到sql重用的目的,如下:
<!-- 传递pojo综合查询用户信息 -->
<select id="findUserList" parameterType="user" resultType="user">
select * from user
<where>
<if test="id!=null and id!=''">
and id=#{id}
</if>
<if test="username!=null and username!=''">
and username like '%${username}%'
</if>
</where>
</select>
- 将where条件抽取出来:
<sql id="query_user_where">
<if test="id!=null and id!=''">
and id=#{id}
</if>
<if test="username!=null and username!=''">
and username like '%${username}%'
</if>
</sql>
- 使用include引用:
<select id="findUserList" parameterType="user" resultType="user">
select * from user
<where>
<include refid="query_user_where"/>
</where>
</select>
注意:如果引用其它mapper.xml的sql片段,则在引用时需要加上namespace,如下:
<include refid="namespace.sql片段”/>
关联查询
一对一关联
- 局部来看,一个订单只能属于一个用户(根据订单查询订单和用户信息)
一对多关联
用户和订单情况,一个用户有多个订单,在user实体类中定义List<User>
生成set/get方法
- mapper.xml
Mybatis整合spring
传统DAO实现
- 传统dao的开发方式
接口+实现类来完成。需要dao实现类需要继承SqlsessionDaoSupport类 - dao实现类
public class UserDaoImpl extends SqlSessionDaoSupport implements UserDao {
@Override
public User findUserById(int id) throws Exception {
SqlSession session = getSqlSession();
User user = session.selectOne("test.findUserById", id);
//不能关闭SqlSession,让spring容器来完成
//session.close();
return user;
}
@Override
public void insertUser(User user) throws Exception {
SqlSession session = getSqlSession();
session.insert("test.insertUser", user);
session.commit();
//session.close();
}
}
- 配置dao
把dao实现类配置到spring容器中
<!-- 配置UserDao实现类 -->
<bean id="userDao" class="cn.itcast.dao.UserDaoImpl">
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
- 初始化:
private ApplicationContext applicationContext;
@Before
public void setUp() throws Exception{
String configLocation = "classpath:spring/ApplicationContext.xml";
//初始化spring运行环境
applicationContext = new ClassPathXmlApplicationContext(configLocation);
}
测试:
@Test
public void testFindUserById() throws Exception {
UserDao userDao = (UserDao) applicationContext.getBean("userDao");
User user = userDao.findUserById(1);
System.out.println(user);
}
动态代理实现
- 开发mapper文配置mapper代理
<!-- 配置mapper代理对象 -->
<bean class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="cn.itcast.mybatis.mapper.UserMapper"/>
<property name="sqlSessionFactory" ref="sqlSessionFactory"></property>
</bean>
- 扫描包形式配置mapper
<!-- 使用扫描包的形式来创建mapper代理对象 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="cn.itcast.mybatis.mapper"></property>
</bean>
注意: 每个mapper代理对象的id就是类名,首字母小写
mybatis plus
mybatis plus是一款专门针对于传统MyBatis开发中sql需要手动进行映射配置繁琐缺点的一款框架技术,这款框架技术提供了十分丰富的api供开发者们使用,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
- pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.sise</groupId>
<artifactId>mybatis-plus</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatisplus-spring-boot-starter</artifactId>
<version>1.0.5</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
<version>2.3</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
</project>
- 通常我们在开发的时候都会自定义一个Dao层,mybatis plus里面提供了一个叫做BaseMapper的接口,内部已经提供了相当多的crud操作函数的封装。可以来仔细查看一下该接口的内容:
/**
* Mapper 继承该接口后,无需编写 mapper.xml 文件,即可获得CRUD功能
*/
public interface BaseMapper<T> {
/**
* 插入一条记录
*/
Integer insert(T entity);
/**
* 插入一条记录
*/
Integer insertAllColumn(T entity);
/**
* 根据 ID 删除
*/
Integer deleteById(Serializable id);
/**
* 根据 columnMap 条件,删除记录
* @param columnMap 表字段 map 对象
*/
Integer deleteByMap(@Param("cm") Map<String, Object> columnMap);
/**
* 根据 entity 条件,删除记录
* @param wrapper 实体对象封装操作类(可以为 null)
*/
Integer delete(@Param("ew") Wrapper<T> wrapper);
/**
* 删除(根据ID 批量删除)
* @param idList 主键ID列表
*/
Integer deleteBatchIds(@Param("coll") Collection<? extends Serializable> idList);
/**
* 根据 ID 修改
* @param entity 实体对象
* @return int
*/
Integer updateById(@Param("et") T entity);
/**
* 根据 ID 修改
* @param entity 实体对象
*/
Integer updateAllColumnById(@Param("et") T entity);
/**
* 根据 whereEntity 条件,更新记录
* @param entity 实体对象
* @param wrapper 实体对象封装操作类(可以为 null)
*/
Integer update(@Param("et") T entity, @Param("ew") Wrapper<T> wrapper);
/**
* 根据 whereEntity 条件,更新记录
* @param setStr set字符串
* @param wrapper 实体对象封装操作类(可以为 null)
*/
Integer updateForSet(@Param("setStr") String setStr, @Param("ew") Wrapper<T> wrapper);
/**
* 根据 ID 查询
* @param id 主键ID
*/
T selectById(Serializable id);
/**
* 查询(根据ID 批量查询)
* @param idList 主键ID列表
* @return List<T>
*/
List<T> selectBatchIds(@Param("coll") Collection<? extends Serializable> idList);
/**
* 查询(根据 columnMap 条件)
* @param columnMap 表字段 map 对象
* @return List<T>
*/
List<T> selectByMap(@Param("cm") Map<String, Object> columnMap);
/**
* 根据 entity 条件,查询一条记录
* @param entity 实体对象
*/
T selectOne(@Param("ew") T entity);
/**
* 根据 Wrapper 条件,查询总记录数
* @param wrapper 实体对象
* @return int
*/
Integer selectCount(@Param("ew") Wrapper<T> wrapper);
/**
* 根据 entity 条件,查询全部记录
* @param wrapper 实体对象封装操作类(可以为 null)
* @return List<T>
*/
List<T> selectList(@Param("ew") Wrapper<T> wrapper);
/**
* 根据 Wrapper 条件,查询全部记录
* @param wrapper 实体对象封装操作类(可以为 null)
* @return List<T>
*/
List<Map<String, Object>> selectMaps(@Param("ew") Wrapper<T> wrapper);
/**
* 根据 Wrapper 条件,查询全部记录
* 注意: 只返回第一个字段的值
* @param wrapper 实体对象封装操作类(可以为 null)
* @return List<Object>
*/
List<Object> selectObjs(@Param("ew") Wrapper<T> wrapper);
/**
* 根据 entity 条件,查询全部记录(并翻页)
* @param rowBounds 分页查询条件(可以为 RowBounds.DEFAULT)
* @param wrapper 实体对象封装操作类(可以为 null)
* @return List<T>
*/
List<T> selectPage(RowBounds rowBounds, @Param("ew") Wrapper<T> wrapper);
/**
* 根据 Wrapper 条件,查询全部记录(并翻页)
* @param rowBounds 分页查询条件(可以为 RowBounds.DEFAULT)
* @param wrapper 实体对象封装操作类
* @return List<Map<String, Object>>
*/
List<Map<String, Object>> selectMapsPage(RowBounds rowBounds, @Param("ew") Wrapper<T> wrapper);
}
- 分页查询
@GetMapping(value = "/selectAllInPage")
public List<Teacher> selectAllInPage(int pageNumber,int pageSize){
Page<Teacher> page =new Page<>(pageNumber,pageSize);
EntityWrapper<Teacher> entityWrapper = new EntityWrapper<>();
entityWrapper.ge("id", 1);
return teacherMapper.selectPage(page,entityWrapper);
}