MyBatis
框架
软件的半成品
Mybatis概述
是一个持久层框架,使用sql语句将实体类映射到数据表,是一个半自动的ORM实现
O:对象
R:关系型数据库
M:映射(自动映射)
Hibernate:全自动的ORM实现
SSH:Struts2、Spring,Hibernate
SSM:SpringMVC、Spring、MyBatis
历史
最早是apache的一个开源项目iBatis,2010年更名为mybatis
特点
外部化sql语句:把sql语句与java代码分离
封装化sql语句:把sql语句封装到单独的文件总,方便管理
使用
创建一个maven项目
配置 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>org.neu</groupId> <artifactId>maven-demo</artifactId> <packaging>war</packaging> <version>1.0-SNAPSHOT</version> <build> <plugins> <!-- 资源文件拷贝插件 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-resources-plugin</artifactId> <version>2.7</version> <configuration> <encoding>UTF-8</encoding> </configuration> </plugin> <!-- java编译插件 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.2</version> <configuration> <source>1.8</source> <target>1.8</target> <encoding>UTF-8</encoding> </configuration> </plugin> </plugins> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.xml</include> </includes> </resource> <resource> <directory>src/main/resources</directory> </resource> </resources> </build> <!-- 依赖 --> <dependencies> <!-- 单元测试 --> <!-- https://mvnrepository.com/artifact/junit/junit --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.6</version> </dependency> <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.22</version> </dependency> <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.20</version> <scope>provided</scope> </dependency> </dependencies> </project>
在resources目录下,添加SqlMapConfig.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> <!-- 配置一个环境,一个环境对应着一个数据库,default为某一个环境的id --> <environments default="mysql"> <environment id="mysql"> <!-- 事务管理器,jdbc/managed(由外部容器管理,自己什么也不做) --> <transactionManager type="jdbc"></transactionManager> <!-- 数据源,unpooled、pooled、jndi --> <dataSource type="pooled"> <property name="url" value="jdbc:mysql://localhost:3306/java15?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai"/> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="username" value="root"/> <property name="password" value="root"/> </dataSource> </environment> </environments> <mappers> <package name="com.neu.mapper"></package> </mappers> </configuration>
Mapper映射文件
编写实体类
创建一个包:com.neu.mapper
在Mapper包下,创建接口:DeptMapper,在该接口中添加增删改查的方法
使用mybatis mapper向导,生成接口的配置映射文件:配置文件的名与接口名相同
在生成的映射文件中(DeptMapper.xml),namespace的名称为对应的接口的完全限定名(包名+接口名)
statement的id与接口中的方法名相同
如果statement为select类型,必须添加一个 resultType 或 resultMap 属性,resultType的值必须为方法返回值中实体类型名
注册 mapper(SqlMapConfig.xml)
<mappers> <!-- <mapper resource="com/neu/mapper/DeptMapper.xml" /> --> <!-- <mapper class="com.neu.mapper.DeptMapper"/> --> <package name="com.neu.mapper"></package> </mappers>
改进后的测试代码
package com.neu.test; import java.io.IOException; import java.io.InputStream; import java.util.List; 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.After; import org.junit.Before; import org.junit.Test; import com.neu.mapper.DeptMapper; import com.neu.po.Dept; public class DeptMapperTest { private DeptMapper deptMapper; private SqlSession sqlSession; @Before public void setup() throws IOException { //读取SqlMapConfig.xml文件 InputStream resource = Resources.getResourceAsStream("SqlMapConfig.xml"); //创建SqlSessionFactory SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resource); //得到Sqlsession对象(相当于数据库连接) sqlSession = sqlSessionFactory.openSession(); //得到DeptMapper接口的实现类 deptMapper = sqlSession.getMapper(DeptMapper.class); } @After public void after() { //关闭sqlSession,把连接放回到连接池 sqlSession.close(); } @Test public void testInsert() throws IOException { int n = deptMapper.insert(new Dept(66,"hr","sy")); System.out.println(n); //提交事务 sqlSession.commit(); } @Test public void testUpdate() throws IOException { int n = deptMapper.update(new Dept(66,"hr","nj")); System.out.println(n); //提交事务 sqlSession.commit(); } @Test public void testDelete() throws IOException { int n = deptMapper.delete(66); System.out.println(n); //提交事务 sqlSession.commit(); } @Test public void testGetById() throws IOException { Dept dept = deptMapper.getById(10); System.out.println(dept); } @Test public void testGetAll() throws IOException { List<Dept> list = deptMapper.getAll(); System.out.println(list); } }
作业
实现 Emp 的增删改查方法
使用log4j生成日志
添加依赖
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
拷贝log4j.properties配置文件到resources下
# Global logging configuration
#\u751F\u4EA7\u73AF\u5883\u914D\u7F6Einfo ERROR
log4j.rootLogger=DEBUG,stdout
# MyBatis logging configuration...
log4j.logger.org.mybatis.example.BlogMapper=TRACE
# 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
多参数的处理
使用参数名:arg0、arg1…
<select id="search" resultType="com.neu.po.Dept"> select * from dept where dname = #{arg0} and loc = #{arg1} </select>
使用参数名:param1、param2
<select id="search" resultType="com.neu.po.Dept"> select * from dept where dname = #{param1} and loc = #{param2} </select>
使用@Param注解(推荐)
接口: List<Dept> search(@Param("dname") String dname,@Param("loc") String loc); mapper: select * from dept where dname = #{dname} and loc = #{loc}
使用实体类封装多个参数
接口: List<Dept> search2(Dept dept); mapper: select * from dept where dname = #{dname} and loc = #{loc} 测试代码: List<Dept> list = deptMapper.search2(new Dept(null,"hr","sy"));
使用map集合,封装多个参数(很灵活)
接口: List<Dept> search3(Map<String, String> map); mapper: select * from dept where dname = #{dname} and loc = #{loc} 测试代码: Map map = new HashMap<>(); map.put("dname", "hr"); map.put("loc", "sy"); List<Dept> list = deptMapper.search3(map);
#{}和${}
#{}:使用占位符的方式生成sql语句,sql语句中列名为变量的时候,不能使用
${}:使用拼接字符串的方式生成sql语句,参数为列名的时候,可以使用
接口: //根据参数列名来排序 List<Dept> order(String columnName); mapper: select * from dept order by ${columnName}
模糊查询
使用#{}
接口: List<Dept> search4(String dname); mapper: select * from dept where dname like #{dname} 测试代码: List<Dept> list = deptMapper.search4("%h%");
使用${}
接口: List<Dept> search4(String dname); mapper: select * from dept where dname like '%${dname}%' 测试代码: List<Dept> list = deptMapper.search4("h");
动态SQL
<if test="表达式">sql片段</if>
当表达式为true的时候,输出sql片段,否则不输出
List<Dept> find(Dept dept); mapper: <select id="find" resultType="com.neu.po.Dept"> select * from dept where 1 = 1 <if test="deptno != null"> and deptno = #{deptno} </if> <if test="dname != null"> and dname = #{dname} </if> <if test="loc != null"> and loc = #{loc} </if> </select>
<trim prefix="前缀" prefixOverrides="被覆盖的前缀" suffixOverrides="被覆盖的后缀"></trim>
prefix:如果trim标签中内容不为空串,则添加该前缀,如果内容为空串,则不添加
prefixOverrides:如果trim标签内容最前面的部分是该属性的内容,则覆盖掉
suffixOverrides:如果trim标签内容最后面的部分是该属性的内容,则覆盖掉
<select id="find" resultType="com.neu.po.Dept"> select * from dept <trim prefix="where" prefixOverrides="and"> <if test="deptno != null"> and deptno = #{deptno} </if> <if test="dname != null"> and dname = #{dname} </if> <if test="loc != null"> and loc = #{loc} </if> </trim> </select> <update id="update2"> update dept <trim prefix="set" suffixOverrides=","> <if test="dname != null" > dname = #{dname}, </if> <if test="loc != null" > loc = #{loc} </if> </trim> where deptno = #{deptno} </update>
<where></where>
,相当于:<trim prefix="where" prefixOverrides="and">
<select id="find" resultType="com.neu.po.Dept"> select * from dept <where> <if test="deptno != null"> and deptno = #{deptno} </if> <if test="dname != null"> and dname = #{dname} </if> <if test="loc != null"> and loc = #{loc} </if> </where> </select>
<set></set>
相当于:<trim prefix="set" suffixOverrides=",">
<update id="update2"> update dept <set> <if test="dname != null" > dname = #{dname}, </if> <if test="loc != null" > loc = #{loc} </if> </set> where deptno = #{deptno} </update>
<foreach></foreach>
:用来处理集合和数组collection:list|conllection|array
item:引用集合或数组中的当前遍历元素
separator:分隔符
接口: int batchInsert(List<Dept> list); mapper: <insert id="batchInsert"> insert into dept values <foreach collection="list" item="dept" separator=","> (#{dept.deptno},#{dept.dname},#{dept.loc}) </foreach> </insert>
open:要添加的开始字符串
close:要添加的结束字符串
接口: int batchDelete(int[] ids); mapper: <delete id="batchDelete"> delete from dept where deptno in <foreach collection="array" item="deptno" separator="," open="(" close=")"> #{deptno} </foreach> </delete>
练习:编写Emp的动态sql
SQL片段
定义:用来定义可以重复使用的sql代码段,可以包含在其他sql语句中
使用:
定义sql片段
<sql id="selectAll"> select * from dept </sql>
引用sql片段
<include refid="selectAll"></include>
得到插入的主键值
<selectKey>
:
order:决定了查询语句执行的先后
- after:先执行插入语句,后执行查询主键的sql语句(mysql)
- before:先执行查询(生成)主键值的语句,再执行插入语句(oracle)
keyProperty:键属性名,查询得到的主键值放到形参的哪个属性中
resultType:查询的主键值的类型
使用:
<insert id="insert">
<selectKey order="AFTER" keyProperty="deptno" resultType="int">
select last_insert_id()
</selectKey>
insert into dept values(null,#{dname},#{loc})
</insert>
查询返回简单数据类型
简单数据类型:基本数据类型(8种)、对应的包装类、String
接口:
//根据部门编号得到部门名称
String getDname(int deptno);
mapper:
<select id="getDname" resultType="string">
select dname from dept where deptno = #{deptno}
</select>
//得到部门总数
int count();
<select id="count" resultType="int">
select count(*) from dept
</select>
返回boolean类型:如果查询的记录数>0,返回:true,等于0,返回:false
//判断名称为参数执行的部门是否存在
boolean exist(String dname);
<select id="exist" resultType="boolean">
select count(*) from dept where dname = #{dname}
</select>
分页
//pageNum:第几页,pageSize:每页最多有多少行
List<Dept> getPaged(@Param("pageNum") int pageNum,@Param("pageSize") int pageSize);
<select id="getPaged" resultType="com.neu.po.Dept">
select * from dept
limit ${(pageNum - 1) * pageSize},#{pageSize}
</select>
列名与属性名不一致
在sql语句中,为列起别名,别名与属性名一致
select deptno,dname dname2,loc from dept
使用
<resultMap>
来进行映射<resultMap type="com.neu.po.Dept" id="basicMap"> <!-- <id column="deptno" property="deptno"/> --> <result column="dname" property="dname2"/> <!-- <result column="loc" property="loc"/> --> </resultMap> <select id="getById" resultMap="basicMap"> <include refid="selectAll"></include> where deptno = #{no} </select>
<id>
:用来映射标识列(数据库中的主键)<result>
:用来映射普通的结果(非主键)column:列名
property:属性名
实体关联
<association>
:关联(实体),表示一对一的关系(emp=》dept)
//Emp实体类
...
Dept dept;
mapper:
<mapper namespace="com.neu.mapper.EmpMapper">
<resultMap type="com.neu.po.Emp" id="basicMap">
<id column="empno" property="empno" ></id>
<result column="ename" property="ename"/>
<result column="job" property="job"/>
<result column="hiredate" property="hiredate"/>
<result column="sal" property="sal"/>
<result column="mgr" property="mgr"/>
<result column="comm" property="comm"/>
<association property="dept" javaType="com.neu.po.Dept">
<id column="deptno" property="deptno"/>
<result column="dname" property="dname2"/>
<result column="loc" property="loc"/>
</association>
</resultMap>
<select id="getById" resultMap="basicMap">
SELECT e.*,dname,loc
FROM dept d JOIN emp e
ON d.deptno = e.deptno
where empno = #{empno}
</select>
</mapper>
<resultMap type="com.neu.po.Dept" id="basicMap">
<id column="deptno" property="deptno" />
<result column="dname" property="dname1"/>
<result column="loc" property="loc"/>
<collection property="emps" ofType="com.neu.po.Emp">
<id column="empno" property="empno"/>
<id column="ename" property="ename"/>
<id column="job" property="job"/>
<id column="hiredate" property="hiredate"/>
<id column="sal" property="sal"/>
<id column="comm" property="comm"/>
<id column="mgr" property="mgr"/>
</collection>
</resultMap>
<select id="getById" resultMap="basicMap">
select d.dname,d.loc,e.* from dept d left join emp e
on d.deptno = e.deptno
WHERE e.deptno = #{deptno}
</select>
实体关联的第二种实现方法:再执行一次查询,根据已有的列查询出需要的对象
<mapper namespace="com.neu.mapper.EmpMapper">
<resultMap type="com.neu.po.Emp" id="basicMap">
<!-- column的值为emp表中的列名 -->
<association property="dept" column="deptno" select="com.neu.mapper.DeptMapper.getById">
</association>
</resultMap>
<select id="getById" resultMap="basicMap">
SELECT * FROM emp where empno = #{empno}
</select>
</mapper>
关联集合
<collection>
:关联集合,表示一对多的关系(dept=》List
<resultMap type="com.neu.po.Dept" id="basicMap">
<id column="deptno" property="deptno"/>
<result column="dname" property="dname2"/>
<!-- <result column="loc" property="loc"/> -->
<!-- column列的值为:Dept表中的deptno -->
<collection property="emps" column="deptno" select="com.neu.mapper.EmpMapper.getByDeptno"></collection>
</resultMap>
练习
CREATE TABLE role(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(30) NOT NULL
) CHARSET=utf8;
INSERT INTO role VALUES(1,'管理员');
INSERT INTO role VALUES(2,'普通用户');
CREATE TABLE myuser(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(30) NOT NULL,
roleId INT NOT NULL
) CHARSET=utf8;
INSERT INTO myuser VALUES(1,'tom',1);
INSERT INTO myuser VALUES(2,'scott',2);
INSERT INTO myuser VALUES(3,'marry',2);
功能:
- 查询所有用户的信息(包括用户的角色信息)
- 根据角色名称查询该角色的信息及该角色下的所有用户信息
使用注解替代Mapper文件
使用注解
@Update("update dept set dname = #{dname2} ,loc = #{loc} where deptno = #{deptno}") int update(Dept dept);
长sql语句的写法
@Update("update dept set dname = #{dname2} ,loc = #{loc} "+ "where deptno = #{deptno}") int update(Dept dept); @Update({"update dept set dname = #{dname2} ,loc = #{loc} ", "where deptno = #{deptno}"}) int update(Dept dept);
ResultMap(mybatis 3.5.4)
@Select("select * from dept where deptno = #{deptno}") @Results(id = "basicMap",value= { @Result(column = "deptno",property = "deptno",id = true), @Result(column = "dname",property = "dname2",id = false), @Result(column = "loc",property = "loc",id = false), @Result(column = "deptno",property = "emps",many=@Many(select = "com.neu.mapper.EmpMapper.getByDeptno")) }) Dept getById(int deptno);
延迟加载
定义:在数据与对象进行mapping操作时,只有在真正使用该对象的时候,才进行mapping操作,以减少数据库开销,从而提高系统性能
默认情况下,mybatis没有开启延迟加载功能,如果要开启,需要配置SqlMapConfig.xml文件
<settings> <!-- 开启延迟加载 --> <setting name="lazyLoadingEnabled" value="true"/> <!-- 取消积极加载 --> <setting name="aggressiveLazyLoading" value="false"/> </settings>
缓存
定义:是一种“以空间换时间”的设计理念,利用内存或硬盘空间来提高数据检索速度的有效手段之一。
一级缓存:默认开启,把数据缓存到当前会话中(SqlSession)
@Test public void testGetById() throws IOException { Dept dept = deptMapper.getById(10); System.out.println(dept.getDname2()); dept = deptMapper.getById(10); System.out.println(dept.getDname2()); }
二级缓存:默认没有开启,需要手工开启,修改SqlMapConfig.xml文件,把数据缓存到SqlSessionFactory中
<!-- 开启二级缓存 --> <setting name="cacheEnabled" value="true"/>
在需要缓存的mapper文件中:
<cache></cache>
注意:缓存对象要实现可序列化接口(Serializable)
MBG
定义:是一个MyBatis的代码生成器,是根据已经创建的数据库表生成实体类、mapper接口、sql映射文件
操作过程
使用向导生成配置文件:generatorConfig.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd"> <generatorConfiguration> <context id="context1" targetRuntime="Mybatis3Simple"> <jdbcConnection connectionURL="jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8" driverClass="com.mysql.jdbc.Driver" password="root" userId="root" /> <javaModelGenerator targetPackage="com.neu.po" targetProject="mybatisdemo2" /> <sqlMapGenerator targetPackage="com.neu.mapper" targetProject="mybatisdemo2"></sqlMapGenerator> <javaClientGenerator targetPackage="com.neu.mapper" targetProject="mybatisdemo2" type="XMLMAPPER" /> <table schema="root" tableName="dept"> <!-- <columnOverride column="???" property="???" /> --> </table> </context> </generatorConfiguration>
排序:
example.setOrderByClause("ename desc");
Example练习
- 查询员工姓名等于“smith”并且sal大于等于3000的员工信息
- 查询员工编号为:7783和7876的员工信息
- 查询奖金(comm)不是null的所有员工信息
- 更新员工编号为7783的薪水,薪水增加200元
- 查询工资小于2000元或部门编号为10的所有员工信息
- 根据部门名称查询该部门所有员工(组合DeptExample和EmpExample查询)
Idea 中使用 Free mybatis plugin
配置数据源
在idea中连接数据库
- 添加数据源,选mysql
配置数据库连接参数
在Advanced 中 配置 serverTimezone:Asia/Shanghai
测试连接
查看数据表
生成代码
选择表
输入参数
Idea环境中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.neu</groupId>
<artifactId>maven-demo3</artifactId>
<version>1.0-SNAPSHOT</version>
<build>
<plugins>
<!-- 资源文件拷贝插件 -->
<!--<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>2.7</version>
<configuration>
<encoding>UTF-8</encoding>
</configuration>
</plugin>-->
<!-- java编译插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.2</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
<!--<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>-->
</build>
<!-- 依赖 -->
<dependencies>
<!-- 单元测试 -->
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.22</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
</project>
Idea中代码生成
方法一
创建 maven 项目,导入依赖和插件
<?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.neu</groupId> <artifactId>mbgdemo</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <!--<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.22</version> </dependency>--> </dependencies> <build> <plugins> <!--mybatis-generator插件--> <plugin> <!--Mybatis-generator插件,用于自动生成Mapper和POJO--> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-maven-plugin</artifactId> <version>1.3.2</version> <configuration> <!--配置文件的位置 一定要改成配置文件的位置--> <configurationFile>src/main/resources/generatorConfig.xml</configurationFile> <verbose>true</verbose> <overwrite>true</overwrite> </configuration> <executions> <execution> <id>Generate MyBatis Artifacts</id> <goals> <goal>generate</goal> </goals> </execution> </executions> <dependencies> <dependency> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-core</artifactId> <version>1.3.2</version> </dependency> </dependencies> </plugin> </plugins> </build> </project>
在 src/main/resources/ 创建文件:generatorConfig.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd"> <generatorConfiguration> <classPathEntry location="D:\mavenrepository\mysql\mysql-connector-java\8.0.22\mysql-connector-java-8.0.22.jar"/> <context id="context1" targetRuntime="Mybatis3"> <jdbcConnection connectionURL="jdbc:mysql://localhost:3306/his_java15?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai" driverClass="com.mysql.cj.jdbc.Driver" password="root" userId="root" > <property name="nullCatalogMeansCurrent" value="true"/> </jdbcConnection> <!-- 实体类 --> <javaModelGenerator targetPackage="com.neu.po" targetProject="src/main/java" /> <!-- 生成Mapper.xml --> <sqlMapGenerator targetPackage="com.neu.mapper" targetProject="src/main/resources"></sqlMapGenerator> <!-- java的Mapper接口 --> <javaClientGenerator targetPackage="com.neu.mapper" targetProject="src/main/java" type="XMLMAPPER" /> <table schema="root" tableName="role"> <generatedKey column="id" sqlStatement="mysql" identity="true"/> </table> <!-- <table schema="root" tableName="user"> <generatedKey column="id" sqlStatement="mysql" identity="true"/> <columnOverride column="useType" property="role" javaType="Role" /> </table> --> <!--<table schema="root" tableName="Department" domainObjectName="Department"> <generatedKey column="id" sqlStatement="mysql" identity="true"/> <columnOverride column="DeptCode" property="deptCode"></columnOverride> <columnOverride column="DeptName" property="deptName"></columnOverride> <columnOverride column="DeptCategoryID" property="deptCategory" javaType="ConstantItem"></columnOverride> <columnOverride column="DeptType" property="deptType" javaType="ConstantItem"></columnOverride> <columnOverride column="DelMark" property="delMark"></columnOverride> </table>--> </context> </generatorConfiguration>
生成代码
方法二
使用
MyBatisCodeHelpPro
插件,生成 xml配置文件修改 xml 配置文件,内容同方法一一样
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd"> <generatorConfiguration> <classPathEntry location="D:\mavenrepository\mysql\mysql-connector-java\8.0.22\mysql-connector-java-8.0.22.jar"/> <context id="Mybatis3Context" targetRuntime="MyBatis3"> <property name="javaFileEncoding" value="UTF-8"/> <jdbcConnection connectionURL="jdbc:mysql://localhost:3306/his_java15?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai" driverClass="com.mysql.cj.jdbc.Driver" password="root" userId="root" > <property name="nullCatalogMeansCurrent" value="true"/> </jdbcConnection> <javaTypeResolver> <property name="forceBigDecimals" value="false"/> </javaTypeResolver> <javaModelGenerator targetPackage="com.neu.po" targetProject="src/main/java"> <property name="enableSubPackages" value="true"/> <property name="trimStrings" value="true"/> </javaModelGenerator> <sqlMapGenerator targetPackage="com.neu.mapper" targetProject="src/main/resources"> <property name="enableSubPackages" value="true"/> </sqlMapGenerator> <javaClientGenerator type="XMLMAPPER" targetPackage="com.neu.mapper" targetProject="src/main/java"> <property name="enableSubPackages" value="true"/> </javaClientGenerator> <table schema="root" tableName="role"> <generatedKey column="id" sqlStatement="mysql" identity="true"/> </table> </context> </generatorConfiguration>
修改 pom.xml文件,添加 maven 插件
<?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.neu</groupId> <artifactId>mgb2</artifactId> <version>1.0-SNAPSHOT</version> <build> <plugins> <!--mybatis-generator插件--> <plugin> <!--Mybatis-generator插件,用于自动生成Mapper和POJO--> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-maven-plugin</artifactId> <version>1.3.2</version> <configuration> <!--配置文件的位置 一定要改成配置文件的位置--> <configurationFile>src/main/resources/MybatisGenerator.xml</configurationFile> <verbose>true</verbose> <overwrite>true</overwrite> </configuration> <executions> <execution> <id>Generate MyBatis Artifacts</id> <goals> <goal>generate</goal> </goals> </execution> </executions> <dependencies> <dependency> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-core</artifactId> <version>1.3.2</version> </dependency> </dependencies> </plugin> </plugins> </build> </project>
生成代码(同方法一)
Idea中生成Mapper
在 mapper 接口上 alt + enter
选择:Generate mapper of xml
选择mapper 位置
MyBatis中TypeHandler
在Mybatis中可以定义一个TypeHandler类型,通过它可以实现Java类型跟数据库类型的相互转换。
需求:把java实体类中的String[],转换为数据库中的varchar类型(例如保存用户的多个爱好)
数据库
CREATE TABLE myuser( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(30) NOT NULL, PASSWORD VARCHAR(30) NOT NULL, interests VARCHAR(100) )
实体类
@Data @AllArgsConstructor @NoArgsConstructor public class MyUser { private int id; private String username; private String password; private String[] interests; }
类型转换器
package com.neu.util; import java.sql.CallableStatement; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Date; import org.apache.ibatis.type.BaseTypeHandler; import org.apache.ibatis.type.JdbcType; import org.apache.ibatis.type.MappedJdbcTypes; import org.apache.ibatis.type.MappedTypes; @MappedJdbcTypes(JdbcType.VARCHAR) @MappedTypes(String[].class) public class StringArrayTypeHandler extends BaseTypeHandler<String[]> { private String[] getStringArray(String columnValue) { if (columnValue == null) return null; return columnValue.split(","); } @Override public String[] getNullableResult(ResultSet rs, String columnName) throws SQLException { return getStringArray(rs.getString(columnName)); } @Override public String[] getNullableResult(ResultSet rs, int columnIndex) throws SQLException { return this.getStringArray(rs.getString(columnIndex)); } @Override public String[] getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { return this.getStringArray(cs.getString(columnIndex)); } @Override public void setNonNullParameter(PreparedStatement ps, int i, String[] parameter, JdbcType jdbcType) throws SQLException { //由于BaseTypeHandler中已经把parameter为null的情况做了处理,所以这里我们就不用再判断parameter是否为空了,直接用就可以了 StringBuffer result = new StringBuffer(); for (String value : parameter) result.append(value).append(","); result.deleteCharAt(result.length()-1); ps.setString(i, result.toString()); } }
1.@MappedJdbcTypes定义是JdbcType类型,必须要是枚举类org.apache.ibatis.type.JdbcType所枚举的数据类型
2.@MappedTypes定义的是JavaType的数据类型,描述哪些Java类型可被拦截。
3.在我们启用了自定义的这个TypeHandler之后,数据的读写都会被这个类所过滤
4.在setNonNullParameter方法中,重新定义要写往数据库的数据。
5.在另外三个方法中我们将从数据库读出的数据类型进行转换。注册 TypeHandler(SqlMapConfig.xml)
<typeHandlers> <package name="com.neu.util"/> </typeHandlers> 或 <typeHandlers> <typeHandler handler="com.neu.util.StringArrayTypeHandler"/> </typeHandlers>
接口
public interface MyUserMapper { MyUser getById(int id); int insert(MyUser user); int update(MyUser user); }
mapper(可以不配置 typeHandler 属性,由mybatis自动选择)
<?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.neu.mapper.MyUserMapper"> <resultMap type="com.neu.po.MyUser" id="basicMap"> <result column="interests" property="interests" typeHandler="com.neu.util.StringArrayTypeHandler" /> </resultMap> <!-- 可以使用 resultMap 进行手工映射 --> <select id="getById" resultType="com.neu.po.MyUser"> select * from myuser where id = #{id} </select> <insert id="insert"> insert into myuser values( #{id}, #{username,javaType=String,jdbcType=VARCHAR,typeHandler=org.apache.ibatis.type.StringTypeHandler}, #{password},#{interests,typeHandler=com.neu.util.StringArrayTypeHandler} ) </insert> <update id="update"> update myuser set username = #{username},password= #{password},interests=#{interests} where id = #{id} </update> </mapper>
测试代码
public class MyUserMapperTest { private SqlSession session; private MyUserMapper myuserMapper; @Before public void setup() throws IOException { //ctrl+2 L //读取配置文件 InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml"); //创建SqlSessionFactory SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //创建连接,相当于Connection session = sessionFactory.openSession(); //返回DeptMapper接口的实现类 myuserMapper = session.getMapper(MyUserMapper.class); } @After public void after() { //关闭会话 session.close(); } @Test public void testInsert() { int n = myuserMapper.insert(new MyUser(33,"tom","111",new String[]{"游泳","爬山"})); System.out.println(n); session.commit(); } @Test public void testGetById() { MyUser user = myuserMapper.getById(10); System.out.println(user); } @Test public void testUpdate() { int n = myuserMapper.update(new MyUser(33,"marry","222",new String[]{"游泳","爬山","钓鱼"})); System.out.println(n); session.commit(); } }
mybatis中 在 TypeHandlerRegistry
注册内置拦截器
public TypeHandlerRegistry(Configuration configuration) {
this.unknownTypeHandler = new UnknownTypeHandler(configuration);
register(Boolean.class, new BooleanTypeHandler());
register(boolean.class, new BooleanTypeHandler());
register(JdbcType.BOOLEAN, new BooleanTypeHandler());
register(JdbcType.BIT, new BooleanTypeHandler());
register(Byte.class, new ByteTypeHandler());
register(byte.class, new ByteTypeHandler());
register(JdbcType.TINYINT, new ByteTypeHandler());
register(Short.class, new ShortTypeHandler());
register(short.class, new ShortTypeHandler());
register(JdbcType.SMALLINT, new ShortTypeHandler());
register(Integer.class, new IntegerTypeHandler());
register(int.class, new IntegerTypeHandler());
register(JdbcType.INTEGER, new IntegerTypeHandler());
register(Long.class, new LongTypeHandler());
register(long.class, new LongTypeHandler());
register(Float.class, new FloatTypeHandler());
register(float.class, new FloatTypeHandler());
register(JdbcType.FLOAT, new FloatTypeHandler());
register(Double.class, new DoubleTypeHandler());
register(double.class, new DoubleTypeHandler());
register(JdbcType.DOUBLE, new DoubleTypeHandler());
register(Reader.class, new ClobReaderTypeHandler());
register(String.class, new StringTypeHandler());
register(String.class, JdbcType.CHAR, new StringTypeHandler());
register(String.class, JdbcType.CLOB, new ClobTypeHandler());
register(String.class, JdbcType.VARCHAR, new StringTypeHandler());
register(String.class, JdbcType.LONGVARCHAR, new StringTypeHandler());
register(String.class, JdbcType.NVARCHAR, new NStringTypeHandler());
register(String.class, JdbcType.NCHAR, new NStringTypeHandler());
register(String.class, JdbcType.NCLOB, new NClobTypeHandler());
register(JdbcType.CHAR, new StringTypeHandler());
register(JdbcType.VARCHAR, new StringTypeHandler());
register(JdbcType.CLOB, new ClobTypeHandler());
register(JdbcType.LONGVARCHAR, new StringTypeHandler());
register(JdbcType.NVARCHAR, new NStringTypeHandler());
register(JdbcType.NCHAR, new NStringTypeHandler());
register(JdbcType.NCLOB, new NClobTypeHandler());
register(Object.class, JdbcType.ARRAY, new ArrayTypeHandler());
register(JdbcType.ARRAY, new ArrayTypeHandler());
register(BigInteger.class, new BigIntegerTypeHandler());
register(JdbcType.BIGINT, new LongTypeHandler());
register(BigDecimal.class, new BigDecimalTypeHandler());
register(JdbcType.REAL, new BigDecimalTypeHandler());
register(JdbcType.DECIMAL, new BigDecimalTypeHandler());
register(JdbcType.NUMERIC, new BigDecimalTypeHandler());
register(InputStream.class, new BlobInputStreamTypeHandler());
register(Byte[].class, new ByteObjectArrayTypeHandler());
register(Byte[].class, JdbcType.BLOB, new BlobByteObjectArrayTypeHandler());
register(Byte[].class, JdbcType.LONGVARBINARY, new BlobByteObjectArrayTypeHandler());
register(byte[].class, new ByteArrayTypeHandler());
register(byte[].class, JdbcType.BLOB, new BlobTypeHandler());
register(byte[].class, JdbcType.LONGVARBINARY, new BlobTypeHandler());
register(JdbcType.LONGVARBINARY, new BlobTypeHandler());
register(JdbcType.BLOB, new BlobTypeHandler());
register(Object.class, unknownTypeHandler);
register(Object.class, JdbcType.OTHER, unknownTypeHandler);
register(JdbcType.OTHER, unknownTypeHandler);
register(Date.class, new DateTypeHandler());
register(Date.class, JdbcType.DATE, new DateOnlyTypeHandler());
register(Date.class, JdbcType.TIME, new TimeOnlyTypeHandler());
register(JdbcType.TIMESTAMP, new DateTypeHandler());
register(JdbcType.DATE, new DateOnlyTypeHandler());
register(JdbcType.TIME, new TimeOnlyTypeHandler());
register(java.sql.Date.class, new SqlDateTypeHandler());
register(java.sql.Time.class, new SqlTimeTypeHandler());
register(java.sql.Timestamp.class, new SqlTimestampTypeHandler());
register(String.class, JdbcType.SQLXML, new SqlxmlTypeHandler());
register(Instant.class, new InstantTypeHandler());
register(LocalDateTime.class, new LocalDateTimeTypeHandler());
register(LocalDate.class, new LocalDateTypeHandler());
register(LocalTime.class, new LocalTimeTypeHandler());
register(OffsetDateTime.class, new OffsetDateTimeTypeHandler());
register(OffsetTime.class, new OffsetTimeTypeHandler());
register(ZonedDateTime.class, new ZonedDateTimeTypeHandler());
register(Month.class, new MonthTypeHandler());
register(Year.class, new YearTypeHandler());
register(YearMonth.class, new YearMonthTypeHandler());
register(JapaneseDate.class, new JapaneseDateTypeHandler());
// issue #273
register(Character.class, new CharacterTypeHandler());
register(char.class, new CharacterTypeHandler());
}
mybatis 类型别名 注册:TypeAliasRegistry
说明:https://blog.csdn.net/lgl782519197/article/details/109066243
public TypeAliasRegistry() {
registerAlias("string", String.class);
registerAlias("byte", Byte.class);
registerAlias("long", Long.class);
registerAlias("short", Short.class);
registerAlias("int", Integer.class);
registerAlias("integer", Integer.class);
registerAlias("double", Double.class);
registerAlias("float", Float.class);
registerAlias("boolean", Boolean.class);
registerAlias("byte[]", Byte[].class);
registerAlias("long[]", Long[].class);
registerAlias("short[]", Short[].class);
registerAlias("int[]", Integer[].class);
registerAlias("integer[]", Integer[].class);
registerAlias("double[]", Double[].class);
registerAlias("float[]", Float[].class);
registerAlias("boolean[]", Boolean[].class);
registerAlias("_byte", byte.class);
registerAlias("_long", long.class);
registerAlias("_short", short.class);
registerAlias("_int", int.class);
registerAlias("_integer", int.class);
registerAlias("_double", double.class);
registerAlias("_float", float.class);
registerAlias("_boolean", boolean.class);
registerAlias("_byte[]", byte[].class);
registerAlias("_long[]", long[].class);
registerAlias("_short[]", short[].class);
registerAlias("_int[]", int[].class);
registerAlias("_integer[]", int[].class);
registerAlias("_double[]", double[].class);
registerAlias("_float[]", float[].class);
registerAlias("_boolean[]", boolean[].class);
registerAlias("date", Date.class);
registerAlias("decimal", BigDecimal.class);
registerAlias("bigdecimal", BigDecimal.class);
registerAlias("biginteger", BigInteger.class);
registerAlias("object", Object.class);
registerAlias("date[]", Date[].class);
registerAlias("decimal[]", BigDecimal[].class);
registerAlias("bigdecimal[]", BigDecimal[].class);
registerAlias("biginteger[]", BigInteger[].class);
registerAlias("object[]", Object[].class);
registerAlias("map", Map.class);
registerAlias("hashmap", HashMap.class);
registerAlias("list", List.class);
registerAlias("arraylist", ArrayList.class);
registerAlias("collection", Collection.class);
registerAlias("iterator", Iterator.class);
registerAlias("ResultSet", ResultSet.class);
}
使用注解替代mapper文件
使用注解
@Select("select * from dept") List<Dept> getAll(); @Insert("insert into dept values(#{deptno},#{dname},#{loc})") int insert(Dept dept);
长 SQL 语句的写法
@Select("select * from dept "+ "where deptno = #{deptno}") Dept getById(int deptno); @Select({"select * from dept ", "where deptno = #{deptno}"}) Dept getById(int deptno);
ResultMap
直接在接口方法上添加@Results注解
//mybatis的版本号:3.5.4,支持@Results中的id属性 @Select({"select * from dept ", "where deptno = #{deptno}"}) @Results(id="basicMap", value= { @Result(column = "deptno",property = "deptno",id = true), @Result(column = "dname",property = "dname"), @Result(column = "loc",property = "loc"), @Result(property = "emps",column = "deptno",many = @Many(select = "com.neu.mapper.EmpMapper.getByDeptno")) }) Dept getById(int deptno); @Select("select * from emp where empno = #{empno}") @Results(value= { @Result( property = "dept", column = "deptno", one = @One(select = "com.neu.mapper.DeptMapper.getById" )) }) Emp getById(int empno);
使用@ResultMap注解,引用@Results
//basicMap可以是@Results的id或对应mapper.xml文件中<resultMap>的id属性 @Select("select * from dept") @ResultMap("basicMap") List<Dept> getAll();
得到自增主键值
@Insert("insert into dept values(null,#{dname},#{loc})") @SelectKey( statement = "select last_insert_id()", resultType = Integer.class, keyProperty = "deptno", before = false ) int insert(Dept dept);
查询补充
//接口
List<Map<String,Object>> report();
//mapper
<select id="report" resultType="map">
select ename,job,hiredate,dname,loc
from emp e join dept d
on e.deptno = d.deptno
</select>
MBG(Mybatis Generator)
定义:是一个Mybatis的代码生成器,是根据已经创建的数据库表生成实体类、mapper接口、sql映射文件
操作过程
使用向导生成配置文件:generatorConfig.xml
修改模板:
改为使用“简单模板”
<context id="context1" targetRuntime="Mybatis3Simple">
使用默认模板
<context id="context1" >
修改配置
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd"> <generatorConfiguration> <context id="context1" targetRuntime="Mybatis3Simple"> <jdbcConnection connectionURL="jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=utf8" driverClass="com.mysql.jdbc.Driver" password="root" userId="root" /> <javaModelGenerator targetPackage="com.neu.po" targetProject="mybatismbg" /> <sqlMapGenerator targetPackage="com.neu.mapper" targetProject="mybatismbg"></sqlMapGenerator> <javaClientGenerator targetPackage="com.neu.mapper" targetProject="mybatismbg" type="XMLMAPPER" /> <table schema="root" tableName="dept"> <!-- <columnOverride column="???" property="???" /> --> </table> <table schema="root" tableName="emp"> <!-- <columnOverride column="???" property="???" /> --> </table> </context> </generatorConfiguration>
生成代码(配置文件右键)
如果有实体关联,修改xml配置文件
修改生成xml配置文件
//generatorConfig.xml <table schema="root" tableName="emp"> <columnOverride column="deptno" property="dept" javaType="com.neu.po.Dept" /> </table>
修改生成的mapper文件
<!-- <result column="deptno" jdbcType="INTEGER" property="dept" /> --> <association property="dept" column="deptno" select="com.neu.mapper.DeptMapper.selectByPrimaryKey"></association>
修改 dept =》 dept.deptno(注意:别修改列名)
使用Mybatis3模板
生成的接口中有11个方法
- 方法名中包含 primaryKey:根据主键进行相应的操作
- 方法名中包含example:根据Example生成where语句来操作
- 方法名中包含Selective:对部分列进行有选择操作(动态sql)
Example的作用:生成where语句
@Test public void testGetByExample() throws IOException { EmpExample example = new EmpExample(); // example.or().andEnameEqualTo("SMITH").andSalGreaterThan(new BigDecimal(700)); // example.or().andEnameLike("%S%"); // example.or().andSalBetween(new BigDecimal(1500), new BigDecimal(2000)); // List<Integer> list = new ArrayList<>(); // list.add(7369); // list.add(7499); // // example.or().andEmpnoIn(list); Dept dept = new Dept(); dept.setDeptno(10); example.or().andDeptEqualTo(dept); List<Emp> list2 = empMapper.selectByExample(example); System.out.println(list2); } //修改EmpExample public Criteria andDeptEqualTo(Dept value) { addCriterion("deptno =", value.getDeptno(), "dept"); return (Criteria) this; }
练习:
- 生成Role和MyUser实体类、接口、mapper文件
- 根据角色Id查询该角色的所有用户信息