赞
踩
环境:
MyBatis 是一款优秀的持久层框架,
它支持自定义 SQL、存储过程以及高级映射。
MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。
MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
github
<?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> <environments default="development"> <!--可以配置多个environment--> <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/school?characterEncoding=utf8"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> //每一个mapper需要配置一份xml的路径 <mappers> <mapper resource="UserMapper.xml"/> <!--mapper...--> </mappers> </configuration>
否则可能会报错找不到UserMapper.xml文件
<!--导出资源文件--> <!-- 资源目录 --> <resources> <resource> <!-- 设定主资源目录 --> <directory>src/main/java</directory> <!-- maven default生命周期,process-resources阶段执行maven-resources-plugin插件的resources目标处理主资源目下的资源文件时,只处理如下配置中包含的资源类型 --> <includes> <include>**/*.xml</include> </includes> <!-- maven default生命周期,process-resources阶段执行maven-resources-plugin插件的resources目标处理主资源目下的资源文件时,不处理如下配置中包含的资源类型(剔除下如下配置中包含的资源类型)--> <excludes> <exclude>**/*.yaml</exclude> </excludes> <!-- maven default生命周期,process-resources阶段执行maven-resources-plugin插件的resources目标处理主资源目下的资源文件时,指定处理后的资源文件输出目录,默认是${build.outputDirectory}指定的目录--> <!--<targetPath>${build.outputDirectory}</targetPath> --> <!-- maven default生命周期,process-resources阶段执行maven-resources-plugin插件的resources目标处理主资源目下的资源文件时,是否对主资源目录开启资源过滤 --> <filtering>true</filtering> </resource> </resources>
每个mapper.xml都需要在mybatis核心配置文件中进行配置
<mappers>
<mapper resource="UserMapper.xml"/>
<!--mapper...-->
</mappers>
package utils; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.*; import java.io.*; public class MybatisUtils { private static SqlSessionFactory sqlSessionFactory; static{ InputStream inputStream = null; try { String resource = "mybatis-config.xml"; inputStream = Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } catch (IOException e) { e.printStackTrace(); } } public static SqlSession getSqlSession(){ return sqlSessionFactory.openSession(); } }
package entity; public class User { private int id; private String name; private String pwd; User(int id,String name,String pwd){ this.id = id; this.name = name; this.pwd = pwd; } 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; } public String getPwd() { return pwd; } public void setPwd(String pwd) { this.pwd = pwd; } }
package dao;
import entity.User;
import java.util.List;
public interface UserMapper {
List<User> getAllUser();
//User selectById(int id);
//int deleteById(int id);
//int UpdateById(int id);
//int InsertUser(User user);
}
<?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="dao.UserMapper">
<select id="getAllUser" resultType="entity.User">
select * from user
</select>
</mapper>
package dao; import entity.User; import org.apache.ibatis.session.SqlSession; import org.junit.Test; import utils.MybatisUtils; import java.util.List; public class UserMapperTest { @Test public void test(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); //方式一,getMapper 推荐使用方式一 UserMapper usermapper = sqlSession.getMapper(UserMapper.class); List<User> users1 = usermapper.getAllUser(); for (User user : users1) { System.out.println(user.getName()); } sqlSession.close(); //方式二,getMapper List<User> users2 = sqlSession.selectList("dao.UserMapper.getAllUser"); for (User user : users2) { System.out.println(user.getName()); } sqlSession.close(); } }
增删改需要提交事务!
mapper接口类增加方法
package dao;
import entity.User;
import java.util.List;
public interface UserMapper {
List<User> getAllUser();
User selectById(int id);
int deleteById(int id);
int UpdateById(User user);
int InsertUser(User user);
}
<select id="getAllUser" resultType="entity.User">
select * from user
</select>
<select id="selectById" resultType="entity.User" parameterType="int">
select * from user where id = #{id}
</select>
<insert id="InsertUser" parameterType="entity.User">
insert into user (id,name,pwd) values (#{id},#{name},#{pwd})
</insert>
测试代码
UserMapper usermapper = sqlSession.getMapper(UserMapper.class);
int row = usermapper.InsertUser(new User(4,"zmy","pwd123"));
sqlSession.commit();//此处为提交事务的代码
System.out.println(row);
sqlSession.close();
<update id="UpdateById" parameterType="entity.User">
update user set name = #{name},pwd=#{pwd} where id = #{id}
</update>
测试代码
UserMapper usermapper = sqlSession.getMapper(UserMapper.class);
int row = usermapper.UpdateById(new User(4,"zmy","pwdpwd123"));
sqlSession.commit();
System.out.println(row);
sqlSession.close();
<delete id="deleteById" parameterType="int">
delete from user where id = #{id}
</delete>
测试代码
UserMapper usermapper = sqlSession.getMapper(UserMapper.class);
int row = usermapper.deleteById(4);
sqlSession.commit();
System.out.println(row);
sqlSession.close();
mybatis-config.xml
<environments default="development">
<environment id="development">
<transactionManager type="JDBC">
<property name="..." value="..."/>
</transactionManager>
<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>
注意一些关键点:
默认环境和环境 ID 顾名思义。 环境可以随意命名,但务必保证默认的环境 ID 要匹配其中一个环境 ID。
事务管理器(transactionManager)
在 MyBatis 中有两种类型的事务管理器(也就是 type="[JDBC|MANAGED]"):
数据源(dataSource)
dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源。
有三种内建的数据源类型(也就是 type="[UNPOOLED|POOLED|JNDI]")
方式一:相对路径
<!-- 使用相对于类路径的资源引用 -->
<mappers>
<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
<mapper resource="org/mybatis/builder/BlogMapper.xml"/>
<mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>
方式二:全路径,不推荐
<!-- 使用完全限定资源定位符(URL) -->
<mappers>
<mapper url="file:///var/mappers/AuthorMapper.xml"/>
<mapper url="file:///var/mappers/BlogMapper.xml"/>
<mapper url="file:///var/mappers/PostMapper.xml"/>
</mappers>
方式三:接口扫描
<!-- 使用映射器接口实现类的完全限定类名 -->
<mappers>
<mapper class="org.mybatis.builder.AuthorMapper"/>
<mapper class="org.mybatis.builder.BlogMapper"/>
<mapper class="org.mybatis.builder.PostMapper"/>
</mappers>
注意点:
方式四:包扫描
<!-- 将包内的映射器接口实现全部注册为映射器 -->
<mappers>
<package name="org.mybatis.builder"/>
</mappers>
注意点:
使用${}进行取值
使用方式
<configuration> <properties resource="db.properties"> <property name="username" value="root"/> <property name="password" value="123456"/> </properties> <environments default="development"> <!--可以配置多个environment--> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <!--value对应properties标签中每一个property的name--> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> </environments> </configuration>
db.properties文件内容如下
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/school
user=root
password=123456
方式一:配置一个实体类的别名
<typeAliases>
<typeAlias alias="Author" type="domain.blog.Author"/>
</typeAliases>
方式二:扫描包
<typeAliases>
<package name="dao"/>
</typeAliases>
每一个在包中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 domain.blog.Author
的别名为 author
;若有注解,则别名为其注解值。看下面的例子:
@Alias("author")
public class Author {
...
}
设置参数 | 描述 | 有效值 | 默认值 |
---|---|---|---|
cacheEnabled | 该配置影响的所有映射器中配置的缓存的全局开关。 | true | false | true |
lazyLoadingEnabled | 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置fetchType 属性来覆盖该项的开关状态。 | true | false | false |
aggressiveLazyLoading | When enabled, any method call will load all the lazy properties of the object. Otherwise, each property is loaded on demand (see also lazyLoadTriggerMethods ). | true | false | false (true in ≤3.4.1) |
multipleResultSetsEnabled | 是否允许单一语句返回多结果集(需要兼容驱动)。 | true | false | true |
useColumnLabel | 使用列标签代替列名。不同的驱动在这方面会有不同的表现, 具体可参考相关驱动文档或通过测试这两种不同的模式来观察所用驱动的结果。 | true | false | true |
useGeneratedKeys | 允许 JDBC 支持自动生成主键,需要驱动兼容。 如果设置为 true 则这个设置强制使用自动生成主键,尽管一些驱动不能兼容但仍可正常工作(比如 Derby)。 | true | false | False |
autoMappingBehavior | 指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示取消自动映射;PARTIAL 只会自动映射没有定义嵌套结果集映射的结果集。 FULL 会自动映射任意复杂的结果集(无论是否嵌套)。 | NONE, PARTIAL, FULL | PARTIAL |
autoMappingUnknownColumnBehavior | Specify the behavior when detects an unknown column (or unknown property type) of automatic mapping target. NONE : Do nothing WARNING : Output warning log (The log level of 'org.apache.ibatis.session.AutoMappingUnknownColumnBehavior' must be set to WARN ) FAILING : Fail mapping (Throw SqlSessionException ) | NONE, WARNING, FAILING | NONE |
defaultExecutorType | 配置默认的执行器。SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(prepared statements); BATCH 执行器将重用语句并执行批量更新。 | SIMPLE REUSE BATCH | SIMPLE |
defaultStatementTimeout | 设置超时时间,它决定驱动等待数据库响应的秒数。 | 任意正整数 | Not Set (null) |
defaultFetchSize | 为驱动的结果集获取数量(fetchSize)设置一个提示值。此参数只可以在查询设置中被覆盖。 | 任意正整数 | Not Set (null) |
safeRowBoundsEnabled | 允许在嵌套语句中使用分页(RowBounds)。 If allow, set the false. | true | false | False |
safeResultHandlerEnabled | 允许在嵌套语句中使用分页(ResultHandler)。 If allow, set the false. | true | false | True |
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.XMLLanguageDriver |
callSettersOnNulls | 指定当结果集中值为 null 的时候是否调用映射对象的 setter(map 对象时为 put)方法,这对于有 Map.keySet() 依赖或 null 值初始化的时候是有用的。注意基本类型(int、boolean等)是不能设置成 null 的。 | true | false | false |
returnInstanceForEmptyRow | MyBatis, by default, returns null when all the columns of a returned row are NULL. When this setting is enabled, MyBatis returns an empty instance instead. Note that it is also applied to nested results (i.e. collectioin and association). Since: 3.4.2 | 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 | JAVASSIST (MyBatis 3.3 or above) |
vfsImpl | 指定VFS的实现 | 自定义VFS的实现的类全限定名,以逗号分隔。 | Not set |
useActualParamName | 允许使用方法签名中的名称作为语句参数名称。 为了使用该特性,你的工程必须采用Java 8编译,并且加上-parameters 选项。(从3.4.1开始) | true | false | true |
一个完整的设置如下:
<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>
错误地使用生命周期和作用域会导致并发问题
SqlSessionFactoryBuilder
SqlSessionFactory
SqlSession
解决数据库字段和实体类属性名称不一致的问题
<resultMap id = "UserMap" type = "User">
<!--
column : 数据库字段
property : 实体类属性
-->
<result column="pwd" property="password"/>
</resultMap>resultMap>
<select id="selectById" resultMap="UserMap" parameterType="int">
select * from user where id = #{id}
</select>
通过settings标签设置日志
日志的实现类有如下几种:SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING
<configuration>
<settings>
<!--STDOUT_LOGGING为标准日志输出-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
</configuration>
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码 log4j.rootLogger=DEBUG,console,file #控制台输出的相关设置 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 #文件输出的相关设置 log4j.appender.file = org.apache.log4j.RollingFileAppender log4j.appender.file.File=./log/kuang.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
<configuration>
<settings>
<!--STDOUT_LOGGING为标准日志输出-->
<setting name="logImpl" value="LOG4J"/>
</settings>
</configuration>
package dao; import org.apache.log4j.Logger; import org.junit.Test; public class UserMapperTest { private Logger logger = Logger.getLogger(UserMapperTest.class); @Test public void test(){ logger.debug("debug:进入test"); logger.info("info:进入test"); logger.error("error:进入test"); } }
效果如下:
1、接口
List<User> getUserByLimit(Map<String,Integer> map);
2、mapper.xml
<select id="getUserByLimit" resultType="entity.User" parameterType="map">
select * from user limit #{startIndex},#{pageSize}
</select>
3、测试
@Test
public void test(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
//方式一,getMapper 推荐使用方式一
UserMapper usermapper = sqlSession.getMapper(UserMapper.class);
Map<String,Integer> map = new HashMap<String,Integer>();
map.put("startIndex",1);
map.put("pageSize",2);
List<User> users1 = usermapper.getUserByLimit(map);
for (User user : users1) {
System.out.println(user);
}
sqlSession.close();
}
1、接口
List<User> getUserByRowbounds();
2、mapper.xml
<select id="getUserByRowbounds" resultType="entity.User">
select * from user
</select>
3、测试
@Test
public void test(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
RowBounds rowBounds = new RowBounds(1, 2);
List<User> users1 = sqlSession.selectList("dao.UserMapper.getUserByRowbounds",null,rowBounds);
for (User user : users1) {
System.out.println(user);
}
sqlSession.close();
}
使用注解开发可以省略掉mapper.xml配置文件。但是在核心配置文件mybatis-config.xml中,需要通过绑定接口的方式来绑定mapper
mybatis-config.xml文件内容如下:
<mappers>
<!--<mapper resource="dao/UserMapper.xml"/>-->
<mapper class="dao.UserMapper"/>
</mappers>
接口:
@Select("select * from user where id = #{id} and name = #{name}")
User selectById(@Param("id")int id,@Param("name")String name);
测试:
@Test
public void test(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.selectById(1,"张三");
System.out.println(user);
sqlSession.close();
}
@Param
#{} 和 ${}的区别:
#{}可以防止sql注入,推荐使用
${}不能防止sql注入
mapper.xml配置文件,使用association标签
<select id="getStudent" resultMap="StudentTeacher">
select s.id as sid,s.name as sname,t.id as tid,t.name as tname from student s,teacher t where s.tid = t.id
</select>
<resultMap id="StudentTeacher" type="student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<association property="teacher" javaType="teacher">
<result property="id" column="tid"/>
<result property="name" column="tname"/>
</association>
</resultMap>
接口StudentMapper.java
List<Student> getStudent();
实体类
package entity;
public class Student {
private int id;
private String name;
private Teacher teacher;
public Student(int id, String name, Teacher teacher) {
this.id = id;
this.name = name;
this.teacher = teacher;
}
...
}
package entity; public class Teacher { private int id; private String name; public Teacher() { } public Teacher(int id, String name) { this.id = id; this.name = name; } ... }
测试
@Test
public void test(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List<Student> studentList = mapper.getStudent();
for (Student student : studentList) {
System.out.println(student);
}
sqlSession.close();
}
mapper.xml配置文件,使用collection标签
<select id="getTeacher" resultMap="TeacherStudent">
select t.id as tid,t.name as tname,s.name as sname,s.id as sid,s.tid as stid from teacher t,student s where t.id = s.tid
</select>
<resultMap id="TeacherStudent" type="Teacher">
<result property="id" column="tid"/>
<result property="name" column="tname"/>
<collection property="students" ofType="student">
<result property="name" column="sname"/>
<result property="id" column="sid"/>
<result property="tid" column="stid"/>
</collection>
</resultMap>
接口TeacherMapper.java
List<Teacher> getTeacher();
实体类
package entity;
import java.util.List;
public class Teacher {
private int id;
private String name;
private List<Student> students;
...
}
package entity; public class Student { private int id; private String name; private int tid; public Student() { } public Student(int id, String name, int tid) { this.id = id; this.name = name; this.tid = tid; } ... }
测试
@Test
public void test(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
List<Teacher> teacherList = mapper.getTeacher();
for (Teacher teacher : teacherList) {
System.out.println(teacher);
}
sqlSession.close();
}
<select id="findActiveBlogWithTitleLike" resultType="Blog">
SELECT * FROM BLOG WHERE state = ‘ACTIVE’
<if test="title != null">
<!--title是*中的一个字段-->
AND title like #{title}
</if>
</select>
<select id="findActiveBlogLike" resultType="Blog">
SELECT * FROM BLOG WHERE state = ‘ACTIVE’
<choose>
<when test="title != null">
AND title like #{title}
</when>
<when test="author != null and author.name != null">
AND author_name like #{author.name}
</when>
<otherwise>
AND featured = 1
</otherwise>
</choose>
</select>
<select id="findActiveBlogLike" resultType="Blog">
SELECT * FROM BLOG
<where>
<if test="state != null">
state = #{state}
</if>
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</where>
</select>
where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。
<update id="updateAuthorIfNecessary">
update Author
<set>
<if test="username != null">username=#{username},</if>
<if test="password != null">password=#{password},</if>
<if test="email != null">email=#{email},</if>
<if test="bio != null">bio=#{bio}</if>
</set>
where id=#{id}
</update>
<select id="selectPostIn" resultType="domain.blog.Post">
SELECT *
FROM POST P
WHERE ID in
<foreach item="item" index="index" collection="list"
open="(" separator="," close=")">
#{item}
</foreach>
</select>
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。