Mybatis
连接数据库的方式可以有传统的jdbc、hibernate方式。然而hibernate的连接效率不高,但是简便;jdbc连接效率高,不过需要自己去编写相关的代码。
JDBC的问题存在于:
1、 驱动包硬编码到代码中
2、 连接信息硬编码到代码中
3、 Sql硬编码到编码中
4、 设置输入参数需要手动编写
5、 输出数据参数需要手动编写
6、 遍历不方便
于是就推出了在JDBC方式和hibernate方式之间的mybatis技术。
Mybatis是一个类似于Hibernate的ORM的持久化框架,支持普通SQL查询,存储过程以及高级映射。Mybatis通过使用简单的XML或注解用于配置和原始映射,将接口和POJO对象映射成数据库中的记录
Mybatis的整体结构:
从图中也可以得出:
1、 mybatis需要两个配置文件
第一类是全局的配置文件:mybatis-config.xml
第二类是映射文件:POJO类名+Mapper.xml 或者放在dao层和dao同一目录为:dao类名+Mapper.xml
2、 通过mybatis的配置文件获得sqlSessionFactory,然后通过sqlSEssionFactoryBuilder来创建对象builder方法获取
3、 通过sqlSessionFactory来获取sqlSession对象
4、 通过执行器Executor执行sql信息
5、 Sql输入参数类型:HashMap、String…基本类型、Pojo类
6、 结果集输出参数类型:HashMap、String…基本类型、Pojo类
实例:
通过一个maven项目来实现对mybatis的crud操作
Mybatis-config.xml:
<?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>
<!-- 扫描properties文件 -->
<properties resource="jdbc.properties" />
<!-- 开启驼峰自动映射文件 -->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<!-- 设置变量值 -->
<typeAliases>
<typeAlias type="mjf.haihan.mybatis.pojo.User" alias="User"/>
</typeAliases>
<!-- 配置mybatis的数据库信息,以后在spring中配置 -->
<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>
<!-- 导入映射文件 -->
<mappers>
<!-- <mapper class="mjf.haihan.mybatis.dao.UserDao"/> 采用dao类来导入映射文件 -->
<!-- <mapper resource="UserMapper.xml"/> --> <!--采用映射文件直接导入 -->
<!-- <mapper url="D://itcast/UserMapper.xml"/> 采用文件的全路径来导入映射文件 -->
<package name="mjf.haihan.mybatis.dao"/> <!-- 采用扫描dao包的dao类导入映射文件 -->
</mappers>
</configuration>
测试类:
package mjf.haihan.dao.test;
import java.io.InputStream;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import mjf.haihan.mybatis.dao.UserDao;
import mjf.haihan.mybatis.pojo.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Before;
import org.junit.Test;
public class UserDaoTest {
private SqlSessionFactory sqlSessionFactory;
private SqlSession session;
private UserDao userDAO;
@Before
public void setUpBefore() throws Exception {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
session = sqlSessionFactory.openSession(true);
userDAO = session.getMapper(UserDao.class);
}
/** 根据id查询用户 */
@Test
public void testquerybyId() {
User user = userDAO.queryById(1L);
System.out.println(user);
}
/** 根据用户名和密码查询用户(占位符) */
@Test
public void testquerybynameandpass1(){
User user = userDAO.queryByNameAndPass1("zhangsan", "123456");
System.out.println(user);
}
/** 根据用户名和密码查询用户(注解) */
@Test
public void testquerybynameandpass2(){
User user = userDAO.queryByNameAndPass2("zhangsan", "123456");
System.out.println(user);
}
/** 查询所有的用户 */
@Test
public void testqueryAll(){
List<User> list = userDAO.queryAll();
for(User user :list){
System.out.println(user);
}
}
/** 使用Map查询用户*/
@Test
public void testqueryMapUser(){
Map<String,String> map = new HashMap<>();
map.put("userName", "zhangsan");
map.put("password", "123456");
User user = userDAO.queryMap(map);
System.out.println(user);
}
/** 添加用户 */
@Test
public void testaddUser(){
User user = new User();
user.setUserName("mao");
user.setPassword("123123");
user.setName("毛竣锋");
user.setAge(20);
user.setSex(1);
Date date = new Date();
user.setBirthday(date);
userDAO.addUser(user);
}
/** 修改用户 */
@Test
public void testupdateUser(){
User user = userDAO.queryById(8L);
user.setName("毛竣锋");
user.setUserName("maojunfeng");
userDAO.updateUser(user);
}
/** 删除用户*/
@Test
public void testdeleteUser(){
userDAO.deleteUser(8L);
}
}
UserDao.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="mjf.haihan.mybatis.dao.UserDao">
<!-- 根据id来查询user -->
<select id="queryById" resultType="User">
SELECT * from tb_user where id =#{id}
</select>
<!-- 一、 根据用户名和密码查询用户 -->
<select id="queryByNameAndPass1" resultType="User">
SELECT * from tb_user where user_name =#{0} and password = #{1}
</select>
<!-- 二、根据用户名和密码查询用户 -->
<select id="queryByNameAndPass2" resultType="User">
SELECT * from tb_user where user_name = #{userName} and password = #{password}
</select>
<!-- 查询所有user -->
<select id="queryAll" resultType="User" >
SELECT * from tb_user
</select>
<!-- 添加用户
useGeneratedKeys : 是否开启自添回填
keyProperty : 对象中的属性名
keyColumn :数据库表的字段名
-->
<insert id="addUser" parameterType="User"
keyColumn="id" keyProperty="id" useGeneratedKeys="true"
>
INSERT INTO tb_user (
user_name,
password,
name,
age,
sex,
birthday,
created,
updated
)
VALUES
(
#{userName},
#{password},
#{name},
#{age},
#{sex},
#{birthday},
NOW(),
NOW()
);
</insert>
<!-- 修改用户 -->
<update id="updateUser" parameterType="User">
UPDATE tb_user set
user_name = #{userName},
password = #{password},
name = #{name},
age = #{age},
sex = #{sex},
birthday = #{birthday},
updated = NOW()
WHERE id =#{id}
</update>
<!-- 删除用户 -->
<delete id="deleteUser">
DELETE FROM tb_user where id = #{id};
</delete>
<!-- hashMap -->
<select id="queryMap" resultType="User">
SELECT * from tb_user where user_name = #{userName} and password = #{password};
</select>
</mapper>
使用mybatis可以不实现dao实现类,实现类:使用不方便,实现的方法都几乎差不多。所以交给mybatis来实现
在dao层的方法中,方法名和映射文件中的id需要一致,如果没有使用dao层的方法,则可以使用sqlsession的方法,参数为namespace和id名:
SqlSession.selectOne(“mjf.haihan.mybatis.selectOne”,1L);
Mybatis的动态代理实现
Namespace的作用:
Mapper中的namespace的定义本身是没有限制的,只要不重复即可,但是如果想要使用Mybatis提供的DAO的动态代理,namespace就必须为DAO接口的全路径
在代码中就只需要定义一个DAO接口就行 ,使用sqlSession的getMapper方法,参数为接口类
userDAO = session.getMapper(UserDao.class);
在Mapper.xml配置文件中,每个方法的定义id必须和DAO层的方法一致
public User queryById(Long id);
<select id="queryByNameAndPass1" resultType="User">
在使用映射文件进行操作时,除了在映射文件定义select、update...语句外,还可以使用注解@select、@update...参数为sql语句
@Select("SELECT * from tb_user where id =#{id}")
Mybatis-config的全局配置文件:
在settings属性:
设置参数 | 描述 | 有效值 | 默认值 |
cacheEnabled | 该配置影响的所有映射器中配置的缓存的全局开关。 | true | false | true |
lazyLoadingEnabled | 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。特定关联关系中可通过设置fetchType属性来覆盖该项的开关状态。 | true | false | false |
aggressiveLazyLoading | 当启用时,带有延迟加载属性的对象的加载与否完全取决于对任意延迟属性的调用;反之,每种属性将会按需加载。 | true | false | true |
multipleResultSetsEnabled | 是否允许单一语句返回多结果集(需要兼容驱动)。 | true | false | true |
useColumnLabel | 使用列标签代替列名。不同的驱动在这方面会有不同的表现,具体可参考相关驱动文档或通过测试这两种不同的模式来观察所用驱动的结果。 | true | false | true |
useGeneratedKeys | 允许 JDBC 支持自动生成主键,需要驱动兼容。如果设置为 true 则这个设置强制使用自动生成主键,尽管一些驱动不能兼容但仍可正常工作(比如 Derby)。 | true | false | False |
autoMappingBehavior | 指定 MyBatis 是否以及如何自动映射指定的列到字段或属性。NONE 表示取消自动映射;PARTIAL 只会自动映射没有定义嵌套结果集映射的结果集。FULL 会自动映射任意复杂的结果集(包括嵌套和其他情况)。 | NONE, PARTIAL, FULL | PARTIAL |
defaultExecutorType | 配置默认的执行器。SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(prepared statements);BATCH 执行器将重用语句并执行批量更新。 | SIMPLE REUSE BATCH | SIMPLE |
defaultStatementTimeout | 设置超时时间,它决定驱动等待数据库响应的秒数。 | Any positive integer | Not Set (null) |
safeRowBoundsEnabled | 允许在嵌套语句中使用行分界(RowBounds)。 | true | false | False |
mapUnderscoreToCamelCase | 是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN 到经典 Java 属性名 aColumn 的类似映射。 | true | false | False |
localCacheScope | MyBatis 利用本地缓存机制(Local Cache)防止循环引用(circular references)和加速重复嵌套查询。默认值为 SESSION,这种情况下会缓存一个会话中执行的所有查询。若设置值为 STATEMENT,本地会话仅用在语句执行上,对相同 SqlSession 的不同调用将不会共享数据。 | SESSION | STATEMENT | SESSION |
jdbcTypeForNull | 当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型。某些驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER。 | JdbcType enumeration. Most common are: NULL, VARCHAR and OTHER | OTHER |
lazyLoadTriggerMethods | 指定哪个对象的方法触发一次延迟加载。 | A method name list separated by commas | equals,clone,hashCode,toString |
defaultScriptingLanguage | 指定动态 SQL 生成的默认语言。 | A type alias or fully qualified class name. | org.apache.ibatis.scripting.xmltags.XMLDynamicLanguageDriver |
callSettersOnNulls | 指定当结果集中值为 null 的时候是否调用映射对象的 setter(map 对象时为 put)方法,这对于有 Map.keySet() 依赖或 null 值初始化的时候是有用的。注意原始类型(int、boolean等)是不能设置成 null 的。 | true | false | false |
logPrefix | 指定 MyBatis 增加到日志名称的前缀。 | Any String | Not set |
logImpl | 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 | SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING | Not set |
proxyFactory | 为 Mybatis 用来创建具有延迟加载能力的对象设置代理工具。 | CGLIB | JAVASSIST | CGLIB |
TypeAliases类型别名属性:
使用该属性,可以将需要设置的输入参数或者输出参数,通过这个别名来简化书写
<typeAliases>
<typeAlias alias="User" type="cn.itcast.mybatis.pojo.User" />
</typeAliases>
<select id="queryByNameAndPass1" resultType="User">
SELECT * from tb_user where user_name =#{0} and password = #{1}
</select>
Typehadnles类型处理器
类型处理器是在设置参数,以及从Result中检索值来匹配java数据类型,Mybatis提供非常多的默认类型处理器,一般情况下都可以满足日常的使用。
类型处理器 | Java 类型 | JDBC 类型 |
BooleanTypeHandler | java.lang.Boolean, boolean | 任何兼容的布尔值 |
ByteTypeHandler | java.lang.Byte, byte | 任何兼容的数字或字节类型 |
ShortTypeHandler | java.lang.Short, short | 任何兼容的数字或短整型 |
IntegerTypeHandler | java.lang.Integer, int | 任何兼容的数字和整型 |
LongTypeHandler | java.lang.Long, long | 任何兼容的数字或长整型 |
FloatTypeHandler | java.lang.Float, float | 任何兼容的数字或单精度浮点型 |
DoubleTypeHandler | java.lang.Double, double | 任何兼容的数字或双精度浮点型 |
BigDecimalTypeHandler | java.math.BigDecimal | 任何兼容的数字或十进制小数类型 |
StringTypeHandler | java.lang.String | CHAR 和 VARCHAR 类型 |
ClobTypeHandler | java.lang.String | CLOB 和 LONGVARCHAR 类型 |
NStringTypeHandler | java.lang.String | NVARCHAR 和 NCHAR 类型 |
NClobTypeHandler | java.lang.String | NCLOB 类型 |
ByteArrayTypeHandler | byte[] | 任何兼容的字节流类型 |
BlobTypeHandler | byte[] | BLOB 和 LONGVARBINARY 类型 |
DateTypeHandler | java.util.Date | TIMESTAMP 类型 |
DateOnlyTypeHandler | java.util.Date | DATE 类型 |
TimeOnlyTypeHandler | java.util.Date | TIME 类型 |
SqlTimestampTypeHandler | java.sql.Timestamp | TIMESTAMP 类型 |
SqlDateTypeHandler | java.sql.Date | DATE 类型 |
SqlTimeTypeHandler | java.sql.Time | TIME 类型 |
ObjectTypeHandler | Any | 其他或未指定类型 |
EnumTypeHandler | Enumeration Type | VARCHAR-任何兼容的字符串类型,作为代码存储(而不是索引) |
EnumOrdinalTypeHandler | Enumeration Type | 任何兼容的 NUMERIC 或 DOUBLE 类型,作为位置存储(而不是代码本身)。 |
Environments
环境配置,可以配置测试环境、开发环境、生产环境,但是在构建SqlSessionFactory时只能选择一个,虽然这种方式也可以做到很方便的分离多个环境,但是在实际使用场景下我们是使用spring来管理数据源环境,大致与spring的数据源管理一样
<!-- 配置mybatis的数据库信息,以后在spring中配置 -->
<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>
Mapper映射文件类似于Hibernate的hbm映射文件,也是需要把该文件导入到全局文件中,导入全局文件有三种方式:Mappers映射文件:
1、 通过resources
直接把在类路径下的mapper文件导入到全局文件
<mappers><mapper resources=”UserMapper.xml” /></mappers>
2、 通过url
把在内存中的mapper文件导入全局文件,需要指定的是全路径
<mappers><mapper url=”D://m/UserMapper.xml” /></mappers>
3、 通过class
通过class引入时,需要把mapper.xml放在跟DAO层的同一个文件夹下,而名称也需要和DAO类名一致 ,引入的是DAO层
<mappers><mapper class=”mjf.dao.UserDAO” /></mappers>
4、 还有一种方式:是通过包扫描的方式去加入到全局文件
对包直接进行扫描,来自动加入mapper文件,该文件也是需要放在同一个DAO层文件夹下的
<mappers><mapper package=”mjf.dao” /></mappers>Mapper.xml的编写:
<mappers>
<!-- <mapper class="mjf.haihan.mybatis.dao.UserDao"/> 采用dao类来导入映射文件 -->
<!-- <mapper resource="UserMapper.xml"/> 采用映射文件直接导入 -->
<!-- <mapper url="D://itcast/UserMapper.xml"/> 采用文件的全路径来导入映射文件 -->
<package name="mjf.haihan.mybatis.dao"/> <!-- 采用扫描dao包的dao类导入映射文件 -->
</mappers>
如果是直接导入类路径下的mapper文件,则不要;如果是导入class,或者是扫描包下的mapper文件,则需要:
sqlSession.getMapper(DAO类);
同时需要在映射文件中的namespace设置为该DAO类的全路径:
<mapper namespace=”mjf.haihan.mybatis.dao.UserDao”>
parameterType的传入参数
1、HashMap类型:
2、String、int…基本数据类型
3、POJO的实体类
如果是以上三种参数,则可以省略不写;其他需要写上
传入参数为多参数时,需要使用定义名或者使用占位符
1、 定义名:@Param
Public void queryByNameandPass(@Param(“username”)String name,@Param(“password”)String password);
2、 使用占位符:从0开始
Select * from table where name=#{0} and password = #{1}