MyBatis入门
MyBatis是一款优秀的持久层框架
支持定制SQL、存储过程以及高级映射;避免了JDBC代码、手动设置参数和获取结果集
MyBatis可使用xml配置文件与注解来配置和映射原生类型、接口和Java的POJO实体类为数据库中的记录
三层架构
应用开发三层架构:
表现层:MVC模式;Model模型-实体类,用于数据的封装与数据的传输;View视图-GUI,用于展示数据;Control控制器-事件,用于流程控制
业务层:事务脚本模式;将一个业务中所有的操作封装成一个方法,同时保证方法中所有的数据库更新操作,即保证同时成功或同时失败。避免部分成功部分失败引起的数据混乱操作。
持久层:DAO模式;建立实体类与数据库表映射,一个类对应一个表,一个属性对应一个列,目的是完成对象数据与关系数据的转换
持久层
MyBatis作为一个持久层框架,目的就是完成持久层中数据持久化的操作,即将内存中存储的断电即失的对象数据持久化的保存到数据库中去
特点
简单灵活、SQL与程序代码分离
- 提供映射标签:支持对象与数据库的ORM字段关系映射
- 提供对象关系映射标签:支持对象关系组建维护
- 提供xml标签:支持编写动态SQL
第一个MyBatis程序
-
搭建环境
- 新建普通Maven项目(检查Maven是否使用本地Maven)
- 删除
src
目录作为父工程使用 - 导入Maven依赖
-
创建模块
-
在父工程文件目录右键新建一个model,选择maven工程
-
在子模块的main目录下resource目录新建mybatis-config.xml
-
XML配置文件:包含获取数据库连接实例的数据源(DataSource)和决定事务范围和控制方式的事务管理器
示例: <?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:核心配置文件--> <configuration> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8"/> <property name="username" value="root"/> <property name="password" value="637625"/> </dataSource> </environment> </environments> </configuration>
-
-
-
MyBatisUtils工具类
基于MyBatis的应用都是以一个SqlSessionFactory的实例为核心,通过SqlSessionFactoryBuilder从xml配置文件的Configuration实例数据库连接中构建出SqlSessionFactory实例
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 java.io.IOException; import java.io.InputStream; public class MyBatisUtils { private static SqlSessionFactory sqlSessionFactory //获取sqlSessionFactory对象,从而生产sqlsession实例 static{ try { String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } catch (IOException e) { e.printStackTrace(); } } //取得sqlsession的工厂类后,获取sqlsession实例 //sqlsession包含了面向数据库执行SQL命令所需的所有方法 public static SqlSession getSqlSession(){ return sqlSessionFactory.openSession(); } }
-
实现代码
-
实体类
创建与数据库表对应的实例类,表名对应类名,列名对应属性
-
IDEA:
alt+insert
快捷键生成有参构造、无参构造、属性的getter于setter以及toString方法
,按住ctrl键实现参数多选例: public class User { private int id; private String name; public User() { } public User(int id, String name) { this.id = id; this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "user{" + "id=" + id + ", name='" + name + '\'' + '}'; } }
-
-
Mapper接口
编写基于一个表操作的Mapper接口,定义操作方法
public interface UserMapper { List<User> getUserList(); }
-
Mapper配置文件
新建一个对应Mapper接口的Mapper配置文件,由Mapper.xml配置文件绑定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"> <!--namespace:命名空间属性,绑定对应的Dao接口--> <mapper namespace="com.flagling.dao.UserDao"> <select id="getUserList" resultType="com.flagling.pojo.User"> select * from users where id = 1 </select> </mapper>
namespace
绑定Mapper接口,在mapper
标签中实现接口对应的数据库操作,id为数据库操作对应的方法名,resultType
为返回结果集的一种类型,可指定为对应的实体类-
在MyBatis-config配置文件中添加Mapper映射
<mappers> <mapper resource="com/flagling/mapper/UserMapper.xml"/> </mappers>
-
Java目录下的Mapper配置文件不一定会被Maven扫描成功,因此需要在父项目与子项目中添加资源过滤的代码
<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>
-
-
测试
规范:在test包中创建与main对应的目录结构,在对应的dao目录下创建接口的test类
-
调用MyBatisUtils工具类获得SqlSession对象
-
SqlSession对象调用getMapper方法,取得Mapper接口的实现类,再调用实现类的方法执行sql语句
-
关闭sqlsession
public class UserDaoTest { @Test public void test(){ //调用MyBatisUtils工具类获得SqlSession对象 SqlSession sqlSession = MyBatisUtils.getSqlSession(); //执行sql //方式一:getMapper UserDao userdao = sqlSession.getMapper(UserDao.class); List<User> userList = userdao.getUserList(); for(User user : userList){ System.out.println(user); } //关闭sqlsession sqlSession.close(); } }
-
-
-
CRUD
完成基础的查询之后,功能的增添只与Mapper接口及其配置文件Mapper.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"> <!--namespace:命名空间属性,绑定对应的Dao接口--> <mapper namespace="com.flagling.dao.UserMapper"> <select id="getUserList" resultType="com.flagling.pojo.User"> select * from mybatis.user; </select> </mapper>
-
namespace:命名空间属性,绑定Mapper接口
-
select:
id:对应namespace中的方法名
resultType:sql语句的返回值类型,一般可设置为对应的实体类
parameterType:参数类型,在SQL语句中参数为
#{参数所属的列值}
<select id="getUserById" parameterType="int" resultType="com.flagling.pojo.User"> select * from mybatis.user where id = #{id}; </select>
-
insert:
<insert id="addUser" parameterType="com.flagling.pojo.User"> insert into mybatis.user (id, name) VALUES (#{id},#{name}); </insert>
-
parameterType为Mapper接口的方法定义传递参数类型,sql语句的参数取值直接与对象的属性名相对应,格式为
#{name}
-
增删改需要提交事务才能完成数据库操作,在代码中为调用sqlsession的commit方法
userdao.addUser(new User(5,"老王")); sqlSession.commit();//提交事务
-
-
delete
<delete id="deleteUser" parameterType="int"> delete from mybatis.user where id = #{id} </delete>
-
update
<update id="updateUser" parameterType="com.flagling.pojo.User"> update mybatis.user set name = #{name} where id = #{id} </update>
-
Map对象
当实体类或数据库中表的字段或参数过多时,应当考虑使用Map替换实体类对象作为方法参数使用,在Mapper配置文件中,参数类型写为
map
,sql语句中参数名为对应的key
,这样可以直接取出map集合中key对应的value值Map<String ,Object> map = new HashMap<String , Object>; map.put("id",1); map.put("name","老李");//定制化map集合,直接取key对应的value值 mapper.addUser(map);//直接传递map集合,
-
模糊查询
-
在Java代码执行时,传递参数左右加通配符%:%参数%
List<User> userlist = mapper.getUserLike("%参数%");
-
在sql拼接中使用通配符
select * from user where name like "%"#{value}"%"
-
-
-
配置解析
- Mybatis-config.xml作为MyBatis的核心配置文件,其中包含了会影响MyBatis行为的设置与属性信息
-
环境配置
<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="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF8&serverTimezone=Asia/Shanghai"/> <property name="username" value="root"/> <property name="password" value="637625"/> </dataSource> </environment> </environments>
可以配置多个环境,但在使用时每个SqlSessionFactory实例只能选择一种环境
<transactionManager type=""/>
为MyBatis的事务管理器,MyBatis中有两种事务管理器- JDBC:直接使用了JDBC的提交与回滚设施
- MANAGED:不回滚或提交一个连接,而是让容器管理事务的整个生命周期,已过时
dataSource
:数据源,即连接数据库操作,MyBatis中有三种内建的数据源类型- UNPOOLED:非连接池连接
- POOLED:连接池
- JNDI
-
属性:properties
编写一个外部配置文件: db.properties
driver=com,mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8 username=root password=637625
通过properties引入外部配置文件
<properties resource="db.properties"> <property name="username" value="root"/> <property name="password" value="637625"/> </properties> <configuration> <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> <mappers> <mapper resource="org/mybatis/example/BlogMapper.xml"/> </mappers> </configuration>
在环境配置中自动读取外部配置文件的值,同时还可以手动添加属性配置,但优先级低于外部配置文件,若同时定义一个字段两个值会优先使用外部配置文件的属性值
-
设置:setting
一个配置完整的setting元素示例:
<settings> <setting name="cacheEnabled" value="true"/> <setting name="lazyLoadingEnabled" value="true"/> <setting name="multipleResultSetsEnabled" value="true"/> <setting name="useColumnLabel" value="true"/> <setting name="useGeneratedKeys" value="false"/> <setting name="autoMappingBehavior" value="PARTIAL"/> <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/> <setting name="defaultExecutorType" value="SIMPLE"/> <setting name="defaultStatementTimeout" value="25"/> <setting name="defaultFetchSize" value="100"/> <setting name="safeRowBoundsEnabled" value="false"/> <setting name="mapUnderscoreToCamelCase" value="false"/> <setting name="localCacheScope" value="SESSION"/> <setting name="jdbcTypeForNull" value="OTHER"/> <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/> </settings>
- cacheEnabled:全局开启或关闭缓存
- logImpl:指定MyBatis日志具体实现,未指定时将自动查找
-
类型别名:typeAliases
类型别名是为Java类型设置一个短名,存在的意义在于用来减少类完全限定名的冗余
-
方式1:手动指定一个实体类的别名
<typeAliass> <typeAlias type="com.flagling.pojo.User" alias="User"/> </typeAliass>
-
方式2:自动扫描一个包下的所有实体类,默认设置一个别名为实体类名的首字母小写格式,若有注解则别名为其注解值
<typeAlias> <package name="com.flagling.pojo"/> </typeAlias>
@Alias("User")//注解别名 public Class User{ }
-
-
映射器
注册绑定Mapper接口的配置文件
方式1:
<!-- 使用相对于类路径的资源引用 --> <mappers> <mapper resource="org/mybatis/builder/AuthorMapper.xml"/> </mappers>
方式2:
<!-- 使用映射器接口实现类的完全限定类名 --> <mappers> <mapper class="org.mybatis.builder.AuthorMapper"/> </mappers>
- 采用方式2时,Mapper接口及配置文件必须同名且必须在同一个包下,否则会加载失败
方式3:
<!-- 将包内的映射器接口实现全部注册为映射器 --> <mappers> <package name="org.mybatis.builder"/> </mappers>
- 采用方式3时与方式2一样,Mapper接口及配置文件必须同名且在同一个·包下,否则会加载失败
-
解决实体类属性名与数据库字段不一致问题
当实体类属性与数据库表中字段一致时,类型处理器自动转译查找数据库表,当属性与字段不一致时,数据库表中无该属性名字段,则会获得null值
解决方法:
-
SQL起别名:(不建议)
select id name as username from mybatis.user where id = #{id};
-
ResultMap:结果集映射
在Mapper配置文件中配置对应的结果集映射,再在sql操作中选择resultmap,简易使用如下
结果集映射:
<resultMap id="UserMap" type="User">//id为sql调用时的ID,type为映射的实体类别名 <result column="id" property="id"/>//column为数据库表的列字段,property为实体类对应的属性 <result column="name" property="username"/> </resultMap>
sql调用:
<select id="getUserList" resultMap="UserMap">//选择返回结果集映射及对应的ID select * from mybatis.user; </select>
结果:
-
-
日志
-
若一个SQL数据库操作出现了异常,需要进行排错,MyBatis提供的最好方式即是提供日志进行排错
-
日志工厂
掌握:
LOG4J:需先在父项目导包
STDOUT_LOGGING:标准日志工厂实现,无需导包
设置:在MyBatis配置文件中settings中设置
<settings> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings>
效果:
-
LOG4J
-
父项目pom.xml导包:
<dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>
-
子项目MyBatis配置文件配置
<settings> <setting name="logImpl" value="LOG4J"/> </settings>
-
LOG4J可以使用log4j.properties配置文件进行各种详细的设置
一个典型的配置文件:
#将等级为DEBUG的日志信息输出到confole与file log4j.rootLogger=DEBUG,console,file #console输出相关设置 log4j.appender.console = org.apache.log4j.ConsoleAppender log4j.appender.console.Target = System.out log4j.appender.console.Threshold = DEBUG log4j.appender.console.layout = org.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern = [%c]-%m%n #file输出相关设置 log4j.appender.file = org.apache.log4j.RollingFileAppender log4j.appender.file.File = ./log/project.log log4j.appender.file.MaxFileSize = 10mb log4j.appender.file.Threshold = DEBUG log4j.appender.file.layout = org.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern = [%p][%d{yy-MM-dd}][%c]%m%n #日志输出级别 log4j.logger.org.mybatis = DEBUG log4j.logger.java.sql = DEBUG log4j.logger.java.sql.Statement = DEBUG log4j.logger.java.sql.ResultSet = DEBUG log4j.logger.java.sql.PreparedStatement = DEBUG
-
测试运行
-
简单使用
-
在要使用Log4j的类中导入包:
import org.apache.log4j.Logger;
-
创建静态日志对象,参数为当前类的class属性
static Logger log = Logger.getLogger(UserMapper.class);
-
常用日志级别
log.info("info:进入testlog4j"); log.debug("debug:进入testlog4j"); log.error("error:进入testlog4j");
-
-
-
-
分页
Limit实现分页
方法中创建一个HashMap存放分页起始地址startIndex与页面数据数量pageSize
HashMap<String, Integer> userMap = new HashMap<String, Integer>(); userMap.put("startIndex",1); userMap.put("pageSize",2);
Mapper配置文件中设置对应参数类型为map,设置对应结果集映射
<select id="getUserLimit" parameterType="map" resultMap="UserMap"> select * from mybatis.user limit #{startIndex},#{pageSize} </select>
HashMap中前两个元素键key必须与sql中参数名一致
-
注解开发
注解开发直接在接口的抽象方法上实现
@Select("select * from user") List<User> getUserList();
需要在核心配置文件绑定接口
<mappers> <mapper class = "com.flagling.dao.UserMapper"/> </mappers>
注解开发使用反射获得接口的class对象,得到接口完整的结构,反向推导调用方法,但MyBatis使用注解开发无法解决实体类属性与数据库表列名不对应的问题。
底层:动态代理‘
-
注解CRUD
-
查询:
@select("select * from user where id = #{id}") User getUserById(@Param("id") int id);
在Mapper中通过注解开发,所有的参数必须通过@Param注解将参数与指定字段进行关联映射,再于sql语句中取值,只要二者对应即可成功取值
-
添加:
@Insert("insert into user(id,name) values (#{id},#{name})"); int addUser(User user);
-
修改
@Update("update user set name = #{name} where id = #{id}") int updateUser(User user);
-
删除
@Delete("delete from user where id = #{uid}") int deleteUser(@Param("uid") int id);
-
事务提交
在工具类中可设置CRUD操作自动提交事务,否则CRUD操作不会生效
public static SqlSession getSqlSeaaion(){ return sqlSessionFactory.openSession(true)//openSession方法重载,传递一个布尔值true表示打开自动提交事务 }
-
#{}与${}
#{}类似预编译,可以防止SQL注入,${}直接拼接
-
```java @Insert("insert into user(id,name) values (#{id},#{name})"); int addUser(User user); ```
-
修改
@Update("update user set name = #{name} where id = #{id}") int updateUser(User user);
-
删除
@Delete("delete from user where id = #{uid}") int deleteUser(@Param("uid") int id);
-
事务提交
在工具类中可设置CRUD操作自动提交事务,否则CRUD操作不会生效
public static SqlSession getSqlSeaaion(){ return sqlSessionFactory.openSession(true)//openSession方法重载,传递一个布尔值true表示打开自动提交事务 }
-
#{}与${}
#{}类似预编译,可以防止SQL注入,${}直接拼接
-