author:空巷
WeChat Applet :Java空巷
QQ: 2399014502
Mail: [email protected]
WeiBo : KongXiang_
WeChat:
MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache
software foundation 迁移到了google code,并且改名为MyBatis 。
2013年11月迁移到Github。
MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。
MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。
MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Ordinary Java Object,普通的 Java对象)映射成数据库中的记录。
持久化是将程序数据在持久状态和瞬时状态间转换的机制。通俗的讲,就是瞬时数据(比如内存中的数据,是不能永久保存的)持久化为持久数据(比如持久化至数据库中,能够长久保存)
就是把数据保存到可以永久保持的存储设备当中。一du般来说,持久更为直接的理解就是对数据库的各种操作,如CRUD(增加,删除,修改,查询),更新等操作
持久化是就是把数据存在磁盘而不是内存。
高级一点:持久化就是将数据从瞬时态转化为持久态,长久保存。
持久层,就是把持久的动作封装成一个独立的层,这是为了降低功能代码之间的关联。创建一个更清晰的抽象,提高代码的内聚力,降低代码的耦合度,从而增强代码的要劳动局生和可重用性。
实现持久层的框架有: JDBC, Hibernate,Mybatis,JPA等技术
写过JDBC没有?你会发现(里头的重复代码太多了),当你一个表里有很多很多的字段时,你的查询,新增等等 语句是不是得写起来格外的不舒服。
触发条件:加载配置文件
处理过程:将SQL的配置信息加载成为一个个MappedStatement对象(包括了传入参数映射配置、执行的SQL语句、结果映射配置),存储在内存中。
触发条件:调用Mybatis提供的API
传入参数:为SQL的ID和传入参数对象
处理过程:将请求传递给下层的请求处理层进行处理。
触发条件:API接口层传递请求过来
传入参数:为SQL的ID和传入参数对象
处理过程:
(A)根据SQL的ID查找对应的MappedStatement对象。
(B)根据传入参数对象解析MappedStatement对象,得到最终要执行的SQL和执行传入参数。
©获取数据库连接,根据得到的最终SQL语句和执行传入参数到数据库执行,并得到执行结果。
(D)根据MappedStatement对象中的结果映射配置对得到的执行结果进行转换处理,并得到最终的处理结果。
(E)释放连接资源。
(4)返回处理结果将最终的处理结果返回。
优点:便于维护管理,不用在java代码中找这些语句;
缺点: JDBC方式可以用打断点的方式调试,但是Mybatis不能,需要通过log4j日志输出日志信息帮助调试,然后在配置文件中修改。
优点:用标签代替编写逻辑代码;
缺点:拼接复杂SQL语句时,没有代码灵活,拼写比较复杂。不要使用变通的手段来应对这种复杂的语句。
优点:保证名称相同,配置好映射关系即可自动映射或者,不配置映射关系,通过配置列名=字段名也可完成自动映射。
缺点:对开发人员所写的SQL依赖很强
优点:接近JDBC,比较灵活。
缺点:对SQL语句依赖程度很高;并且属于半自动,数据库移植比较麻烦,比如mysql数据库编程Oracle数据库,部分的sql语句需要整。
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(10) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
`gender` varchar(2) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=36 DEFAULT CHARSET=utf8;
<dependencies>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.16.18version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.7version>
<scope>testscope>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.5.2version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.47version>
dependency>
dependencies>
<build>
<resources>
<resource>
<directory>src/main/javadirectory>
<includes>
<include>**/*.propertiesinclude>
<include>**/*.xmlinclude>
includes>
<filtering>falsefiltering>
resource>
<resource>
<directory>src/main/resourcesdirectory>
<includes>
<include>**/*.propertiesinclude>
<include>**/*.xmlinclude>
includes>
<filtering>falsefiltering>
resource>
resources>
build>
/**
* @author 空巷
* @Date 2020/5/28
*/
//使用了lombok
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User implements Serializable {
private int id;
private String name;
private int age;
private String gender;
}
<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/sys?
useSSL=true&useUnicode=true&characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
dataSource>
environment>
environments>
configuration>
<configuration>
<properties>properties>
<settings>
<setting name="" value=""/>
settings>
<typeAliases>typeAliases>
<typeHandlers>typeHandlers>
<objectFactory type="">objectFactory>
<plugins>
<plugin interceptor="">plugin>
plugins>
<environments default="">
<environment id="">
<transactionManager type="">transactionManager>
<dataSource type="">dataSource>
environment>
environments>
<databaseIdProvider type="">databaseIdProvider>
<mappers>mappers>
configuration>
在 MyBatis 中 settings 是最复杂的配置,它能深刻影响 MyBatis 底层的运行,但是在大部分情况下使用默认值便可以运行,所以在大部分情况下不需要大量配置它,只需要修改一些常用的规则即可,比如自动映射、驼峰命名映射、级联规则、是否启动缓存、执行器(Executor)类型等。settings 配置项说明,如表 1 所示。
配置项 | 作用 | 配置选项 | 默认值 |
---|---|---|---|
cacheEnabled | 该配置影响所有映射器中配置缓存的全局开关 | true|false | true |
lazyLoadingEnabled | 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。在特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态 | true|false | false |
aggressiveLazyLoading | 当启用时,对任意延迟属性的调用会使带有延迟加载属性的对象完整加载;反之,每种属性将会按需加载 | true|felse | 版本3.4.1 (不包含) 之前 true,之后 false |
multipleResultSetsEnabled | 是否允许单一语句返回多结果集(需要兼容驱动) | true|false | true |
useColumnLabel | 使用列标签代替列名。不同的驱动会有不同的表现,具体可参考相关驱动文档或通过测试这两种不同的模式来观察所用驱动的结果 | true|false | true |
useGeneratedKeys | 允许JDBC 支持自动生成主键,需要驱动兼容。如果设置为 true,则这个设置强制使用自动生成主键,尽管一些驱动不能兼容但仍可正常工作(比如 Derby) | true|false | false |
autoMappingBehavior | 指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示取消自动映射。 PARTIAL 表示只会自动映射,没有定义嵌套结果集和映射结果集。 FULL 会自动映射任意复杂的结果集(无论是否嵌套) | NONE、PARTIAL、FULL | PARTIAL |
autoMappingUnkno wnColumnBehavior | 指定自动映射当中未知列(或未知属性类型)时的行为。 默认是不处理,只有当日志级别达到 WARN 级别或者以下,才会显示相关日志,如果处理失败会抛出 SqlSessionException 异常 | NONE、WARNING、FAILING | NONE |
defaultExecutorType | 配置默认的执行器。SIMPLE 是普通的执行器;REUSE 会重用预处理语句(prepared statements);BATCH 执行器将重用语句并执行批量更新 | SIMPLE、REUSE、BATCH | SIMPLE |
defaultStatementTimeout | 设置超时时间,它决定驱动等待数据库响应的秒数 | 任何正整数 | Not Set (null) |
defaultFetchSize | 设置数据库驱动程序默认返回的条数限制,此参数可以重新设置 | 任何正整数 | Not Set (null) |
safeRowBoundsEnabled | 允许在嵌套语句中使用分页(RowBounds)。如果允许,设置 false | true|false | false |
safeResultHandlerEnabled | 允许在嵌套语句中使用分页(ResultHandler)。如果允许,设置false | true|false | true |
mapUnderscoreToCamelCase | 是否开启自动驼峰命名规则映射,即从经典数据库列名 A_COLUMN 到经典 Java 属性名 aColumn 的类似映射 | true|false | false |
localCacheScope | MyBatis 利用本地缓存机制(Local Cache)防止循环引用(circular references)和加速联复嵌套査询。 默认值为 SESSION,这种情况下会缓存一个会话中执行的所有查询。若设置值为 STATEMENT,本地会话仅用在语句执行上,对相同 SqlScssion 的不同调用将不会共享数据 | SESSION|STATEMENT | SESSION |
jdbcTypeForNull | 当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型。某些驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER | NULL、VARCHAR、OTHER | OTHER |
lazyLoadTriggerMethods | 指定哪个对象的方法触发一次延迟加载 | — | equals、clone、hashCode、toString |
defaultScriptingLanguage | 指定动态 SQL 生成的默认语言 | — | org.apache.ibatis .script.ing.xmltags .XMLDynamicLanguageDriver |
callSettersOnNulls | 指定当结果集中值为 null 时,是否调用映射对象的 setter(map 对象时为 put)方法,这对于 Map.kcySet() 依赖或 null 值初始化时是有用的。注意,基本类型(int、boolean 等)不能设置成 null | true|false | false |
logPrefix | 指定 MyBatis 增加到日志名称的前缀 | 任何字符串 | Not set |
loglmpl | 指定 MyBatis 所用日志的具体实现,未指定时将自动査找 | SLF4J|LOG4J|LOG4J2|JDK_LOGGING |COMMONS_LOGGING |ST DOUT_LOGGING|NO_LOGGING | Not set |
proxyFactory | 指定 MyBatis 创建具有延迟加栽能力的对象所用到的代理工具 | CGLIB|JAVASSIST | JAVASSIST (MyBatis 版本为 3.3 及以上的) |
vfsImpl | 指定 VFS 的实现类 | 提供 VFS 类的全限定名,如果存在多个,可以使用逗号分隔 | Not set |
useActualParamName | 允许用方法参数中声明的实际名称引用参数。要使用此功能,项目必须被编译为 Java 8 参数的选择。(从版本 3.4.1 开始可以使用) | true|false | true |
<settings>
<setting name="logImpl" value="LOG4J"/>
<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>
在 MyBatis 中,运行环境主要的作用是配置数据库信息,它可以配置多个数据库,一般而言只需要配置其中的一个就可以了。
它下面又分为两个可配置的元素:事务管理器(transactionManager)、数据源(dataSource)。
在实际的工作中,大部分情况下会采用 Spring 对数据源和数据库的事务进行管理,这些我们教程后面都会进行讲解。本节我们会探讨 MyBatis 自身实现的类。
<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/sys?
useSSL=true&useUnicode=true&characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
dataSource>
environment>
environments>
mappers的存在就是要对写好的mapper和xml进行统一管理
<mappers>
<mapper resource="mapper/userMapper.xml"/>
<mapper resource="mapper/GradeMapper.xml"/>
<mapper resource="mapper/StudentMapper.xml"/>
<mapper class="com.xinzhi.dao.AdminMapper"/>
mappers>
别名 | 映射的类型 |
---|---|
_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 |
object | Object |
map | Map |
hashmap | HashMap |
list | List |
arraylist | ArrayList |
collection | Collection |
iterator | Iterator |
在核心配置文件(mybatis-config.xml)中加入
<typeAliases>
<package name="com.xinzhi.entity"/>
typeAliases>
标签中 有 type 和 alias 两个属性
type 填写 实体类的全类名, alias 可以不填,不填的话,默认是类名,不区分大小写,
alias 填了的话就以 alias里的值为准。
标签 为某个包下的所有类起别名; name 属性填写包名。 别名默认是类名,不区分大小写。
dao层写个接口类(UserMapper)
/**
* @author 空巷
* @Date 2020/5/28
*/
public interface UserMapper {
/**
* 通过id查询用户
* @return
*/
User selectUserById(int id);
}
eg:
一个接口类Mapper (就是Dao)不需要实现
一个接口对应一个xml文件
两个文件名字最好一致
当你建了一个 xxxMapper.xml 配置文件后,需要去mybatis 核心配置文件中注册
<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/sys?
useSSL=true&useUnicode=true&characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
dataSource>
environment>
environments>
<mappers>
<mapper resource="mapper/userMapper.xml"/>
mappers>
configuration>
xxxMapper.xml配置文件格式
<mapper namespace="com.xinzhi.dao.UserMapper">
<select id="selectUserById" resultType="User">
select id,name,age,gender from user where id = #{id}
select>
mapper>
请注意:
#{ }和 ${ }有啥区别
测试:
**eg:**当需要测试时需要加个头和尾 当增删查改时,都需要添加事务(我直接运用了junit测试类 避免代码重复 下面有解释)
private SqlSession session;
@Before
public void before() {
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream("mybatis-config.xml");
} catch (IOException e) {
e.printStackTrace();
}
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
session = sqlSessionFactory.openSession();
}
@After
public void after() {
//统一提交事务
session.commit();
session.close();
}
//通过id查询用户
@org.junit.Test
public void selectUserById() {
UserMapper userMapper = session.getMapper(UserMapper.class);
User user = userMapper.selectUserById(5);
System.out.println(user);
}
接口类
/**
* 添加用户
* @param user
* @return
*/
int addUser(User user);
UserMapper.xml
<insert id="addUser" parameterType="User">
insert into user(`name`,age,gender) VALUES (#{name},#{age},#{gender})
insert>
测试:
//添加用户
@org.junit.Test
public void addUser() {
UserMapper userMapper = session.getMapper(UserMapper.class);
User user = new User(10,"aaa",20,"男");
int i = userMapper.addUser(user);
System.out.println(i);
}
接口类:
/**
* 这是用通过id修改用户的方法
* @param user
* @return
*/
int updateUser(User user);
UserMapper.xml
<update id="updateUser" parameterType="User">
UPDATE `user` name = #{name}, age = #{age}, gender = #{gender} WHERE id = #{id}
update>
测试:
//通过id修改用户
@org.junit.Test
public void updateUserById(){
UserMapper userMapper = session.getMapper(UserMapper.class);
User user = new User();
user.setId(4);
user.setName("zxcjlk");
user.setAge(20);
user.setGender("男");
userMapper.updateUser(user);
}
接口类:
/**
* 根据id删除用户
* @param id
* @return
*/
int deleteUser(int id);
UserMapper.xml
<delete id="deleteUser" parameterType="int">
DELETE FROM `user` WHERE id = #{id}
delete>
测试:
//删除用户
@org.junit.Test
public void deleteUser(){
UserMapper userMapper = session.getMapper(UserMapper.class);
int i = userMapper.deleteUser(6);
System.out.println(i);
}
整理一下:
SqlSessionFactoryBuilder().build(inputStream)-读取核心配置文件,
核心配置文件负责核心配置,重要的一点是通过核心配置找到Mapper的xml文件。
xml文件通过 namespace 又能找到 对应的接口文件。
有了方法,有了sql,会用动态代理,生成代理类,实现对象的方法。
session.getMapper(UserMapper.class) 获取的就是代理对象,当然就有了对应的实现。
结论多个参数时,使用@Param注解,能制定对应的参数,当然如果能封装对象最好封装对象
例如:
/**
* 新增用户
* @param userId
* @param name
* @param pws
* @return
*/
int addUser(@Param("id") int id,@Param("username") String name,@Param("password")
String password);
方案一:在Java代码中拼串
string name = “%IT%”;
list names = mapper.getUserByName(name);
方案二:在配置文件中拼接
<select id=”getUsersByName”>
select * from user where name like "%"#{name}"%"
select>
map可以替代任何实体类,当数据比较复杂时,可以适当考虑使用map来完成相关的工作
接口类
/**
* 使用根据用户查询用户
* @param name
* @return
*/
List<User> getUsersByParams(Map<String,Object> name);
UserMapper.xml
<select id="getUsersByParams" resultType="map">
select id,name,age,gender from user where name = #{name}
select>
测试:
//使用map
@org.junit.Test
public void getUsersByParams(){
UserMapper userMapper = session.getMapper(UserMapper.class);
Map<String,Object> map = new HashMap<String, Object>();
map.put("name","张三");
map.put("id",1);
List<User> usersByParams = userMapper.getUsersByParams(map);
System.out.println(usersByParams);
}
当数据库的字段名和变量名不一致时 例如 数据库里 username 变量名 name
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private int id; //id
private String name; //姓名,数据库为username
private String password; //密码,一致
}
mapper
//根据id查询用户
User selectUserById(int id);
UserMapper.xml
<select id="selectUserById" resultType="user">
select * from user where id = #{id}
select>
测试:
@Test
public void testSelectUserById() {
UserMapper mapper = session.getMapper(UserMapper.class);
User user = mapper.selectUserById(1);
System.out.println(user);
session.close();
}
结果发现:
查询出来 name 为 null 说明
mybatis会根据这些查询的列名(会将列名转化为小写,数据库不区分大小写) , 利用反射去对应的实体类中查找相应列名的set方法设值 ,肯定找不到username;
解决方案一:
可以想想,是不是只需要改变数据库的字段名使得和变量名一致即可
<select id="selectUserById" resultType="User">
select id , username as name ,password from user where id = #{id}
select>
解决方案二:【推荐使用】
使用结果集映射 ResultMap
<resultMap id="UserMap" type="User">
<id column="id" property="id"/>
<result column="username" property="name"/>
<result column="password" property="password"/>
resultMap>
<select id="selectUserById" resultMap="UserMap">
select id , username , password from user where id = #{id}
select>
我这里直接就写代码了 很简单,通过代码来感受
接口中加入注解:
/**
* @author 空巷
* @Date 2020/5/28
*/
public interface AdminMapper {
/**
* 通过id查询用户
* @return
*/
@Select("select id,name,age,gender from admin where id = #{id}")
Admin selectAdminById(int id);
/**
* 添加用户
* @param Admin
* @return
*/
@Insert("insert into admin(`name`,age,gender) VALUES (#{name},#{age},#{gender})")
int addAdmin(Admin Admin);
/**
* 这是用通过id修改用户的方法
* @param Admin
* @return
*/
@Update("UPDATE `admin` SET `name` = #{name},age = #{age},gender = #{gender} WHERE id = #{id}")
int updateAdmin(Admin Admin);
/**
* 根据id删除用户
* @param id
* @return
*/
@Delete("DELETE FROM `admin` WHERE id = #{id}")
int deleteAdmin(int id);
/**
* 根据用户名查询用户
* @param name
* @return
*/
@Select("select id,name,age,gender from admin where name like #{name}")
List<Admin> getAdminByName(String name);
/**
* 使用根据用户查询用户
* @param name
* @return
*/
@Select("select id,name,age,gender from admin where name = #{name}")
List<Admin> getAdminsByParams(Map<String,Object> name);
}
核心配置文件中加入(mybatis-config.xml)
<mappers>
<mapper class="com.xinzhi.dao.AdminMapper"/>
mappers>
进行测试:
/**
* @author 空巷
* @Date 2020/5/28
*/
public class TestAdmin {
private SqlSession session;
@Before
public void before() {
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream("mybatis-config.xml");
} catch (IOException e) {
e.printStackTrace();
}
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
session = sqlSessionFactory.openSession();
}
//通过id查询用户
@org.junit.Test
public void selectAdminById() {
AdminMapper AdminMapper = session.getMapper(AdminMapper.class);
Admin Admin = AdminMapper.selectAdminById(1);
System.out.println(Admin);
}
//添加用户
@org.junit.Test
public void addAdmin() {
AdminMapper AdminMapper = session.getMapper(AdminMapper.class);
Admin Admin = new Admin(6,"aaa",20,"男");
int i = AdminMapper.addAdmin(Admin);
System.out.println(i);
}
//通过id修改用户
@org.junit.Test
public void updateAdminById(){
AdminMapper AdminMapper = session.getMapper(AdminMapper.class);
Admin Admin = new Admin(7,"zxcas",20,"男");
int i = AdminMapper.updateAdmin(Admin);
System.out.println(i);
}
//删除用户
@org.junit.Test
public void deleteAdmin(){
AdminMapper AdminMapper = session.getMapper(AdminMapper.class);
int i = AdminMapper.deleteAdmin(8);
System.out.println(i);
}
//模糊查询
@org.junit.Test
public void getAdminByName(){
AdminMapper AdminMapper = session.getMapper(AdminMapper.class);
List<Admin> AdminByName = AdminMapper.getAdminByName("%张%");
System.out.println(AdminByName);
}
//使用map
@org.junit.Test
public void getAdminsByParams(){
AdminMapper AdminMapper = session.getMapper(AdminMapper.class);
Map<String,Object> map = new HashMap<String, Object>();
map.put("name","张三");
List<Admin> AdminsByParams = AdminMapper.getAdminsByParams(map);
System.out.println(AdminsByParams);
}
@After
public void after() {
//统一提交事务
session.commit();
session.close();
}
}
整体看起来是不是清爽了很多? 但是个人不建议使用,因为它不方便维护。
MyBatis 的强大特性之一便是它的动态 SQL。如果你有使用 JDBC 或其他类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句有多么痛苦。拼接的时候要确保不能忘了必要的空格,还要注意省掉列名列表最后的逗号。利用动态 SQL 这一特性可以彻底摆脱这种痛苦。
if相当于java中的if语句(判断语句)举个例子:
<select id="selectUserById" resultType="User">
select id,name,age,gender from user where 1=1
<if test="id != null and id != ''">
AND id = #{id}
if>
<if test="name != null and name != ''">
AND name = #{name}
if>
<if test="age != null and age != ''">
AND age = #{age}
if>
<if test="gender != null and gender != ''">
AND gender = #{gender}
if>
select>
有些时候我们还需要多种条件的选择,在Java中我们可以使用switch、case、default语句,而在映射器的动态语句
中可以使用choose、when、otherwise元素。
<select id="getUserByName" resultType="User">
select id,name,age,gender from user where 1=1
<choose>
<when test="name != null and name != ''">
AND name like #{name}
when>
<when test="id != null">
AND id = #{id}
when>
choose>
select>
上面的select语句我们加了一个 1=1 的绝对true的语句,目的是为了防止语句错误,变成 SELECT * FROM
student WHERE 这样where后没有内容的错误语句。这样会有点奇怪,此时可以使用 元素。
<select id="selectUserById" resultType="User">
select id,name,age,gender from user
<where>
<if test="id != null and id != ''">
AND id = #{id}
if>
<if test="name != null and name != ''">
AND name = #{name}
if>
<if test="age != null and age != ''">
AND age = #{age}
if>
<if test="gender != null and gender != ''">
AND gender = #{gender}
if>
where>
select>
有时候我们要去掉一些特殊的SQL语法,比如常见的and、or,此时可以使用trim元素。trim元素意味着我们需要
去掉一些特殊的字符串,prefix代表的是语句的前缀,而prefixOverrides代表的是你需要去掉的那种字符串,
suffix表示语句的后缀,suffixOverrides代表去掉的后缀字符串。
<select id="select" resultType="com.xinzhi.entity.User">
SELECT * FROM user
<trim prefix="WHERE" prefixOverrides="AND">
<if test="username != null and username != ''">
AND username LIKE concat('%', #{username}, '%')
if>
<if test="id != null">
AND id = #{id}
if>
trim>
select>
在update语句中,如果我们只想更新某几个字段的值,这个时候可以使用set元素配合if元素来完成。注意:set元
素遇到,会自动把,去掉。
<update id="updateUser" parameterType="User">
UPDATE `user`
<set>
<if test="name != null">
name = #{name},
if>
<if test="age != null">
age = #{age},
if>
<if test="gender != null">
gender = #{gender}
if>
set>
WHERE id = #{id}
update>
foreach元素是一个循环语句,它的作用是遍历集合,可以支持数组、List、Set接口。
<select id="select" resultType="com.xinzhi.entity.User">
SELECT * FROM user
WHERE id IN
<foreach collection="ids" open="(" close=")" separator="," item="id">
#{id}
foreach>
select>
有时候可能某个 sql 语句我们用的特别多,为了增加代码的重用性,简化代码,我们需要将这些代码抽取出来,然
后使用时直接调用。
<sql id="update-user">
<set>
<if test="name != null">
name = #{name},
if>
<if test="age != null">
age = #{age},
if>
<if test="gender != null">
gender = #{gender}
if>
set>
sql>
<update id="updateUser" parameterType="User">
UPDATE `user`
<include refid="update-user"/>
WHERE id = #{id}
update>
有时候我们设计表的时候,需要添加外键,一对多,多对一等关系。
先来设计个数据库。
CREATE TABLE `grade` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`class_name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
CREATE TABLE `student` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`stu_num` varchar(255) DEFAULT NULL,
`name` varchar(255) DEFAULT NULL,
`s_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `g_id` (`s_id`),
CONSTRAINT `g_id` FOREIGN KEY (`s_id`) REFERENCES `grade` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
针对这种关系型表,有如下三种处理办法
不就是多写几个sql语句吗?通过逻辑,进行手动拼装。
实体类:(在这里我已班级和学生为例 一个班级有多个学生)
/**
* @author 空巷
* @Date 2020/5/29
* 班级表
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Grade implements Serializable {
private int id;
//班级名称
private String className;
}
/**
* @author 空巷
* @Date 2020/5/29
* 学生表
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student implements Serializable {
private int id;
private String stuNum;
private String name;
private Grade grade;
}
对应的Mapper接口类
/**
* @author 空巷
* @Date 2020/5/29
*/
public interface GradeMapper {
/**
* 通过id获取班级
* @param id
* @return
*/
Grade getGradeById(int id);
}
/**
* @author 空巷
* @Date 2020/5/29
*/
public interface StudentMapper {
/**
* 获取所有的班级
* @return
*/
List<Student> getAllStudent();
}
编写对应的mapper.xml配置文件
<mapper namespace="com.xinzhi.dao.StudentMapper">
<resultMap id="StudentGrade" type="com.xinzhi.entity.Student">
<association property="grade" column="s_id" javaType="com.xinzhi.entity.Student" select="getGrade"/>
resultMap>
<select id="getAllStudent" resultMap="StudentGrade">
SELECT * FROM student
select>
<select id="getGrade" resultType="Grade">
SELECT * FROM grade WHERE id = #{id}
select>
mapper>
测试:
/**
* @author 空巷
* @Date 2020/5/29
*/
public class TestStudent {
private SqlSession session;
@Before
public void before(){
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream("mybatis-config.xml");
} catch (IOException e) {
e.printStackTrace();
}
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
session = sqlSessionFactory.openSession();
}
@Test
public void testGetAllStudent(){
StudentMapper studentMapper = session.getMapper(StudentMapper.class);
List<Student> allStudent = studentMapper.getAllStudent();
System.out.println(allStudent);
}
@After
public void after(){
session.commit();
session.close();
}
}
切记:得再核心配置文件中加入mapper
<mapper resource="mapper/GradeMapper.xml"/>
<mapper resource="mapper/StudentMapper.xml"/>
即可查询结果。
实体类:
/**
* @author 空巷
* @Date 2020/5/29
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Grade implements Serializable {
private int id;
private String className;
List<Student> students;
}
/**
* @author 空巷
* @Date 2020/5/29
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student implements Serializable {
private int id;
private String stuNum;
private String name;
}
对应的mapper接口类
/**
* @author 空巷
* @Date 2020/5/29
*/
public interface GradeMapper {
/**
* 通过id获取班级
* @param id
* @return
*/
Grade getGradeById(int id);
}
/**
* @author 空巷
* @Date 2020/5/29
*/
public interface StudentMapper {
/**
* 获取所有的班级
* @return
*/
List<Student> getAllStudent();
}
mapper.xml配置文件
<mapper namespace="com.xinzhi.dao.GradeMapper">
<cache type="org.mybatis.caches.ehcache.EhcacheCache" eviction="LRU"
flushInterval="10000" size="1024" readOnly="true"/>
<select id="getGradeById" resultMap="grade">
SELECT s.id sid,s.stu_num stuNum,s.`name` sName,g.class_name,g.id gid,g.class_name className
from grade g,student s WHERE s.s_id = g.id AND g.id = #{id}
select>
<resultMap id="grade" type="Grade">
<id property="id" column="gid"/>
<result property="className" column="className"/>
<collection property="students" ofType="Student">
<result property="id" column="sid"/>
<result property="stuNum" column="stuNum"/>
<result property="name" column="sName"/>
collection>
resultMap>
mapper>
测试:
/**
* @author 空巷
* @Date 2020/5/29
*/
public class TestGrade {
private SqlSession session;
SqlSessionFactory sqlSessionFactory;
@Before
public void before(){
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream("mybatis-config.xml");
} catch (IOException e) {
e.printStackTrace();
}
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
session = sqlSessionFactory.openSession();
}
@Test
public void testGetStudentById(){
GradeMapper mapper = session.getMapper(GradeMapper.class);
Grade gradeById = mapper.getGradeById(1);
System.out.println(gradeById);
}
@After
public void after(){
session.commit();
session.close();
}
}
在执行了上述一些列操作后,你会发现 sql语句看不到 报错了不知道。因此我们需要配置一个日志,把sql语句输出出来,查看一下中间的过程。
指定 MyBatis 应该使用哪个日志记录实现。如果此设置不存在,则会自动发现日志记录实现。
STD:standard out:输出
STDOUT_LOGGING:标准输出日志
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
settings>
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<version>1.2.17version>
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
去核心配置文件中加入 setting
<settings>
<setting name="logImpl" value="LOG4J"/>
settings>
测试 看控制台
缓存会单独出一篇博文。