写在前面
Mybatis框架之前在学校有教过(Ps:吕丽华老师讲的不错)上课比较认真主动课后自己也手把手去反复练习,所以学的效果还算可以,至少其中的增删改查\高级映射\理论概念这块吃的还比较透,后期整合spring/springmvc也比较轻松。但那时只停留在课程阶段没在实际项目中运用过加上那时没有很全的知识体系结构,只知道如何实现,不知道为什么可以这样实现对其中的一些原理不太明白,现在手上项目在做和mybatis天天打交道正巧项目一期快进入尾声了,活也相对轻松,正好趁热打铁对其中不太明白容易搞混不好理解的点拿出来捋一捋清一清。
推荐个参考学习的网址:http://www.mybatis.org/mybatis-3/zh/getting-started.html,里面讲的不错是它的说明文档,各个方面都很细,值得好好学学。
目录结构
Mybatis是什么?
MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生类型、接口和 Java 的 POJO(PlainOld Java Objects,普通老式 Java 对象)为数据库中的记录。
JDBC介绍
JDBC:Java DataBaseConnectivity,是SUN公司提供的一套操作数据库的标准规范(技术)。
JDBC在我们大二上已经学习过了,那时就感觉好神奇可以增删改查了可以和数据库有联动,也没有在乎具体是咋回事里面的原理是什么,现如今回过头去看不免感叹如果现在的项目用这样的代码去实现,我估计开发的人都要疯了,太多重复低效率的工作了。。。。一个简单的查询语句,需要手写的参数那么多,一个po原子类属性多一点的话,后面不敢想象…..
现在再回过头来看jdbc,发现简单了不少,就4个主要核心对象。
DriverManager类:用于注册驱动(创建连接对象)。 java.sql.DriverManager;
Connection接口:表示与数据库创建的连接 。java.sql.Connection;
Statement接口:操作数据库sql语句的对象,并返回相应结果的对象。java.sql.Statement;
preparedStatement接口:预编译对象,是Statement对象的子类。用于解决sql的注入问题。实际用的是这个类。java.sql.PreparedStatement;
ResultSet接口:结果集或一张虚拟表(客户端存表数据的对象)。 java.sql.ResultSet;
public class DatabaseConnection {
//定义数据库驱动程序
public String cond="com.mysql.jdbc.Driver";
//数据库连接地址
public String conc="jdbc:mysql://localhost:3306/new?characterEncoding=utf-8";
public String username="root";
public String password="";
public Connection con;
public DatabaseConnection() throws Exception{
//加载数据库驱动程序
Class.forName(cond);
//取得数据库连接
con=DriverManager.getConnection(conc,username,password);
}
public Connection getConnection(){
return this.con;
}
public void Close() throws Exception{
this.con.close();
}
}
换做Mybatis的话大大减少了这些重复低效率的代码,只需少许xml配置便可以替代上面的内容。学校里介绍了2种mybatis开发Dao的方法。顺带回顾一下,有利于建设整个知识体系结构。
原始dao开发方法(程序员需要写dao接口和dao实现类)
Dao接口
public interface UserDao {
//根据id查询用户信息
public User findUserById(int id) throws Exception;
//添加用户信息
public void insertUser(User user) throws Exception;
//删除用户信息
public void deleteUser(int id) throws Exception;
}
Dao接口实现类
public class UserDaoImpl implements UserDao {
// 需要向dao实现类中注入SqlSessionFactory
// 这里通过构造方法注入
private SqlSessionFactory sqlSessionFactory;
public UserDaoImpl(SqlSessionFactory sqlSessionFactory) {
this.sqlSessionFactory = sqlSessionFactory;
}
@Override
public User findUserById(int id) throws Exception {
SqlSession sqlSession = sqlSessionFactory.openSession();
User user = sqlSession.selectOne("test.findUserById", id);
// 释放资源
sqlSession.close();
return user;
}
@Override
public void insertUser(User user) throws Exception {
SqlSession sqlSession = sqlSessionFactory.openSession();
//执行插入操作
sqlSession.insert("test.insertUser", user);
// 提交事务
sqlSession.commit();
// 释放资源
sqlSession.close();
}
@Override
public void deleteUser(int id) throws Exception {
SqlSession sqlSession = sqlSessionFactory.openSession();
//执行插入操作
sqlSession.delete("test.deleteUser", id);
// 提交事务
sqlSession.commit();
// 释放资源
sqlSession.close();
}
测试代码
public class Main {
//创建fac 后续用到了就调一次 不用不传递
private SqlSessionFactory fac;
@Before
public void before() throws IOException{
//得到配置文件流
InputStream input = Resources.getResourceAsStream("sqlconfig.xml");
//创建会话工厂 传入mybatis配置文件信息
fac = new SqlSessionFactoryBuilder().build(input);
}
@Test
public void function1() throws IOException{
UserDao dao = new UserDaoImpl(fac);
User user = dao. findUserById(1);
System.out.println(user);
}
………
………省略
}
问题如下:
①dao接口实现类方法中存在大量模板方法,设想能否将这些代码提取出来,大大减轻程序员的工作量。
②调用sqlsession方法时传入的变量,由于sqlsession方法使用泛型,即使变量类型传入错误,在编译阶段也不报错,不利于程序员开发。
mapper代理方法(程序员只需要mapper接口(相当于dao接口))
mapper代理开发规范
①在mapper.xml中namespace等于mapper接口地址
②mapper.java接口中的方法名和mapper.xml中statement的id一致
③mapper.java接口中的方法输入参数类型和mapper.xml中statement的parameterType指定的类型一致。
④mapper.java接口中的方法返回值类型和mapper.xml中statement的resultType指定的类型一致。
总结:以上开发规范主要是对下边的代码进行统一生成:
User user =sqlSession.selectOne("test.findUserById", id);
mapper.java
mapper.xml
在SqlMapConfig.xml中加载mapper.xml
测试
*基于Java 注解开发mapper
映射的语句可以不用 XML 来配置,而可以使用 Java 注解来配置。
package org.mybatis.example;
public interface UserMapper{
@Select("SELECT * FROM USER WHERE id = #{value}")
Blog selectBlog(int id);
}
SqlSessionFactory
上述两种方法,其实底层都用到了SqlSessionFactory会话工厂来创建一个SqlSession。
学习Mybatis就是学习如何创建一个SqlSessionFactory,学习如何生成一个Mapper代理实例,学习Mapper里各种输入输出映射关系以及增删改查方法。
SqlSession是一个面向用户(程序员)的接口,提供了很多操作数据库的方法如:selectOne(返回单个对象)、selectList(返回单个或多个对象)、getMapper(生成Mapper代理对象)
每个基于MyBatis 的应用都是以一个SqlSessionFactory 的实例为核心的。SqlSessionFactory 的实例可以通过SqlSessionFactoryBuilder获得。而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先定制的Configuration 的实例构建出 SqlSessionFactory 的实例。
从 XML 文件中构建 SqlSessionFactory 的实例非常简单,建议使用类路径下的资源文件进行配置。但是也可以使用任意的输入流(InputStream)实例,包括字符串形式的文件路径或者 file:// 的 URL 形式的文件路径来配置。MyBatis 包含一个名叫 Resources 的工具类,它包含一些实用方法,可使从 classpath 或其他位置加载资源文件更加容易。
String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
sqlSession.selectOne()....
sqlSession.selectList()....
sqlSession.getMapper()....
sqlSession.insert()....
sqlSession.delete()....
sqlSession.update()....
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>
//properties(属性)
//settings(全局配置参数)
//typeAliases(类型别名)
//typeHandlers(类型处理器)
//objectFactory(对象工厂)
//plugins(插件)
//环境集合属性对象
<environments default="development">
//环境子属性对象
<environment id="development">
//事务管理
<transactionManager type="JDBC"/>
//数据源
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
//(数据库厂商标识)databaseIdProvider
//映射器
<mappers>
//加载单个mapper
<mapper resource="org/mybatis/example/BlogMapper.xml"/>
//通过mapper接口加载单个mapper 前提:①接口文件和映射文件 在一起②接口文件 名字 改为映射文件同名③使用mapper代理方法
<mapper class="包名.类名"/>
//批量加载mapper ----------同上一种的前提
</mappers name="包名">
</mappers>
</configuration>
properties特性:
将数据库连接参数单独配置在db.properties中,只需要在SqlMapConfig.xml配置文件中加载db.properties的属性值。在SqlMapConfig.xml中就不需要对数据库连接参数硬编码。
原因:方便对参数进行统一管理,其它xml可以引用该db.properties。
注意:MyBatis 将按照以下的顺序来加载属性:
①在properties 元素体内定义的属性首先被读取。
<properties resource="org/mybatis/example/config.properties">
//元素体内定义的属性
<property name="username" value="dev_user"/>
<property name="password"value="F2Fa3!33TYyg"/>
</properties>
②然后会读取properties 元素中resource或 url 加载的属性,它会覆盖已读取的同名属性。
③最后读取作为方法参数传递的属性,并覆盖已读取的同名属性。(最高优先级)
建议:
①不要在properties元素体内添加任何属性值,只将属性值定义在properties文件中。
②在properties文件中定义属性名要有一定的特殊性,如:XXXXX.XXXXX.XXXX
小贴士:
从MyBatis 3.4.2 开始,你可以为占位符指定一个默认值。
<!-- 如果属性 'username' 没有被配置,'username' 属性的值将为 'ut_user' -->
<propertyname="username" value="${username:ut_user}"/>
typeAliases(别名)
在mapper.xml中,定义很多的statement,statement需要parameterType指定输入参数的类型、需要resultType指定输出结果的映射类型。
如果在指定类型时输入类型全路径,不方便进行开发,可以针对parameterType或resultType指定的类型定义一些别名,在mapper.xml中通过别名定义,方便开发。
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 |
批量定义别名
<typeAliases>
//指定包名,mybatis自动扫描包中的po类,自动定义别名,别名就是类名(首字母大小写均可)
<package name="domain.blog"/>
</typeAliases>
类型处理器(typeHandlers)
作用:mybatis中通过typeHandlers完成jdbc类型和java类型的转换。
实现方法:
①实现TypeHandler接口(https://www.jb51.net/article/147179.htm)
②继承BaseTypehandler类
(https://blog.csdn.net/huey2672/article/details/38151607)
自定义类型处理器
//setParameter:通过preparedStatement对象设置参数,将T类型的数据存入数据库
void setParameter(PreparedStatement var1, int var2, T var3,JdbcType var4) throws SQLException;
//通过列名或者下标来获取结果数据
T getResult(ResultSet var1, String var2) throws SQLException;
T getResult(ResultSet var1, int var2) throws SQLException;
//通过列名或者下标来获取结果数据,也可以通过CallableStatement获取数据
T getResult(CallableStatement var1, int var2) throws SQLException;
MyTypeHandler实现TypeHandler接口
package com.mdd.mybatis.typehandle;
import com.mdd.mybatis.util.DESUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class MyTypeHandle implements TypeHandler<String> {
private static String KEY = "123456";
@Override
public void setParameter(PreparedStatement preparedStatement, int i, String s, JdbcType jdbcType) throws SQLException {
try {
String encrypt = DESUtil.encrypt(s, KEY);
preparedStatement.setString(i, encrypt);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public String getResult(ResultSet resultSet, String s) throws SQLException {
String result = resultSet.getString(s);
if (StringUtils.isNotEmpty(result)) {
try {
return DESUtil.decrypt(result, KEY);
} catch (Exception e) {
e.printStackTrace();
}
}
return result;
}
@Override
public String getResult(ResultSet resultSet, int i) throws SQLException {
String result = resultSet.getString(i);
if (StringUtils.isNotEmpty(result)) {
try {
return DESUtil.decrypt(result, KEY);
} catch (Exception e) {
e.printStackTrace();
}
}
return result;
}
@Override
public String getResult(CallableStatement callableStatement, int i) throws SQLException {
String result = callableStatement.getString(i);
if (StringUtils.isNotEmpty(result)) {
try {
return DESUtil.decrypt(result, KEY);
} catch (Exception e) {
e.printStackTrace();
}
}
return result;
}
}
配置注册自定义处理器
<!--自定义类型处理器-->
<typeHandlers>
<typeHandler handler="com.mdd.mybatis.typehandle.MyTypeHandle"></typeHandler>
</typeHandlers>
使用自定义处理器(mapper文件)
<result column="name" property="name" typeHandler="com.mdd.mybatis.typehandle.MyTypeHandle"/>
在参数和结果映射中指明哪是 VARCHAR 类型的字段, 以使其能够绑定到正确的类型处理器上。
①在类型处理器的配置元素(typeHandler 元素)上增加一个 javaType 属性(比如:javaType="String");
②在类型处理器的类上(TypeHandler class)增加一个 @ MappedJdbcTypes注解来指定与其关联的 Java 类型列表。如果在 javaType 属性中也同时指定,则注解方式将被忽略。
映射器(mappers)
我们需要告诉 MyBatis 到哪里去找到定义 SQL 映射语句。
通过resource加载单个映射文件
通过mapper接口加载单个mapper
按照上边的规范,将mapper.java和mapper.xml放在一个目录 ,且同名。
批量加载mapper(推荐使用)
未完待续.....
点“在看”你懂得