Mybatis一:理论知识和增删改查
- 1 概述
- 1.1 简介
- 1.2 核心对象
- 1.3 mybatis配置文件
- 1.4 mybatis映射文件
- 2 入门程序
- 2.1 xml方式
- 2.2 annotation方式
- 3 增删改查
- 3.1 xml方式
- 3.2 annotation方式
1 概述
1.1 简介
- mybatis是个优秀的基于java的持久层框架,它内部封装了jdbc,使开发者只需要关注SQL语句本身,而不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。
- mybatis通过xml或annotation的方式将要执行的各种statement配置起来,并通过java对象和statement中SQL的动态参数进行映射生成最终执行的SQL语句,最后由mybatis框架执行SQL并将结果映射为java对象并返回。
- 采用ORM(Object Relational Mappging对象关系映射)思想解决了实体和数据库映射的问题,对jdbc进行了封装,屏蔽了jdbc api底层访问细节,使我们不用与jdbc api打交道,就可以完成对数据库的持久化操作。
1.2 核心对象
-
在使用MyBatis框架时,主要涉及两个核心对象:SqlSessionFactory和SqlSession,它们在MyBatis框架中起着至关重要的作用,图解如下,下面进行详解。
-
SqlSessionFactory
-
SqlSessionFactory是单个数据库映射关系经过编译后的内存镜像,主要作用是创建SqlSession。
-
SqlSessionFactory是线程安全的,一旦被创建整个应用执行期间都会存在。如果多次的创建同一个数据库的SqlSessionFactory,那么数据库资源很容易被耗尽,因此在构建SqlSessionFactory实例时,一般使用单例模式。
// 创建SqlSessionFactory代码 InputStream inputStream = Resources.getResourceAsStream("配置文件位置"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
-
-
SqlSession
-
SqlSession是应用程序与持久层之间执行交互操作的一个单线程对象,是线程不安全的。SqlSession使用完后,要及时关闭它。
-
SqlSession方法如下(了解即可,IDEA中可以点击ctrl+鼠标左键查看)
// 注:若使用mybatis的传统开发方式(即编写接口的实现类)需要用到以下方法,但不推荐使用此方式,推荐用xml或annotation public interface SqlSession extends Closeable { // statement是在配置文件中,
-
1.3 mybatis配置文件
-
主要元素【注:必须按照以下顺序配置,否则会报错(可以缺,但不能乱)】
-
元素 -
元素 -
元素-
typeAliases用于定义别名,减少全限定类名的冗余。
-
alias属性的属性值就是自定义的别名,可以代替com.li.domain.User使用在mybatis文件中的任何位置。如果alias属性省略,mybatis会默认将类名首字母小写后的名称作为别名。
-
-
元素-
该元素可以配置多种数据源,即配置多种数据库。
-
environments标签的default属性用于指定默认的环境。environment是environments的子标签,可以定义多个(即配置多种数据库);transactionManager是environment的子标签,用于配置事务(有JDBC和MANAGED两种);dataSource也是environment的子标签,用于配置数据源(有UNPOOLED、POOLED常用、JNDI三种)。
-
样例
-
-
元素-
该元素用于指定mybatis映射文件的位置,有以下四种配置方式
-
注意路径中的"/"与".",一般前两种比较常用(资源用斜杠,类用点)
-
-
其他元素不太常用,略。等用到时再总结。
1.4 mybatis映射文件
-
主要元素
-
insert、delete、update、select元素【insert的元素中的特殊属性用法(其他元素略)】
-
resultMap
-
元素表示结果映射集,是MyBatis中最重要也是最强大的元素。它的主要作用是定义映射规则、级联的更新、定义类型转化器等。 -
包含的子元素如下
-
样例
-
2 入门程序
-- SQL脚本如下
CREATE TABLE `t_user` (
`id` int(11) NOT NULL auto_increment,
`username` varchar(32) NOT NULL COMMENT '用户名称',
`birthday` datetime default NULL COMMENT '生日',
`sex` char(1) default NULL COMMENT '性别',
`address` varchar(256) default NULL COMMENT '地址',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into `t_user`(`id`,`username`,`birthday`,`sex`,`address`)
values (41,'张三','2018-02-27 17:47:08','男','北京'),
(42,'小明','2018-03-02 15:09:37','女','北京金燕龙'),
(43,'小明','2018-03-04 11:34:34','女','北京金燕龙'),
(45,'李四','2018-03-04 12:04:06','男','北京金燕龙'),
(46,'王五','2018-03-07 17:37:26','男','北京'),
(48,'赵六','2018-03-08 11:44:00','女','北京修正');
2.1 xml方式
-
普通maven工程,结构图如下
-
依赖
org.mybatis mybatis 3.5.5 mysql mysql-connector-java 8.0.21 log4j log4j 1.2.17 org.projectlombok lombok 1.18.22 -
实体类
package cn.pojo; import lombok.ToString; import java.util.Date; @ToString public class User { private Integer id; private String username; private Date birthday; private String six; private String address; }
-
持久层
package cn.dao; import cn.pojo.User; import java.util.List; public interface UserDao { List
findAll(); /** * 注:在实际发开中,都是越简单越好,故不采用写Dao实现类的方式,使用xml或annotation都行。 * 虽然不推荐写Dao实现类,但是mybatis是支持写实现类的。 */ } -
配置
# resources/log4j.properties,把下面内容直接粘贴过去即可 # Set root category priority to INFO and its only appender to CONSOLE. #log4j.rootCategory=INFO, CONSOLE debug info warn error fatal log4j.rootCategory=debug, CONSOLE, LOGFILE # Set the enterprise logger category to FATAL and its only appender to CONSOLE. log4j.logger.org.apache.axis.enterprise=FATAL, CONSOLE # CONSOLE is set to be a ConsoleAppender using a PatternLayout. log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n # LOGFILE is set to be a File appender using a PatternLayout. log4j.appender.LOGFILE=org.apache.log4j.FileAppender log4j.appender.LOGFILE.File=d:\axis.log log4j.appender.LOGFILE.Append=true log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout log4j.appender.LOGFILE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n
<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="UTF-8"?>
-
运行
package cn; import cn.dao.UserDao; import cn.pojo.User; 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; import java.util.List; public class App { public static void main(String[] args) throws IOException { //1.读取配置文件 InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml"); //2.创建SqlSessionFactory工厂 SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream); //3.使用工厂生产SqlSession对象 SqlSession session = factory.openSession(); //4.使用SqlSession创建Dao接口的代理对象 UserDao userDao = session.getMapper(UserDao.class); //5.使用代理对象执行方法 List
users = userDao.findAll(); for (User user : users) { System.out.println(user); } //6.释放资源 session.close(); inputStream.close(); } /** * 结果 * User(id=41, username=张三, birthday=Tue Feb 27 17:47:08 CST 2018, six=null, address=北京) * User(id=42, username=小明, birthday=Fri Mar 02 15:09:37 CST 2018, six=null, address=北京金燕龙) * User(id=43, username=小明, birthday=Sun Mar 04 11:34:34 CST 2018, six=null, address=北京金燕龙) * User(id=45, username=李四, birthday=Sun Mar 04 12:04:06 CST 2018, six=null, address=北京金燕龙) * User(id=46, username=王五, birthday=Wed Mar 07 17:37:26 CST 2018, six=null, address=北京) * User(id=48, username=赵六, birthday=Thu Mar 08 11:44:00 CST 2018, six=null, address=北京修正) */ }
2.2 annotation方式
-
普通maven工程,结构图如下
-
依赖
org.mybatis mybatis 3.5.5 mysql mysql-connector-java 8.0.21 log4j log4j 1.2.17 junit junit 4.12 org.projectlombok lombok 1.18.22 -
实体类
- 同 xml 方式实体类
-
持久层
package cn.dao; import cn.pojo.User; import org.apache.ibatis.annotations.Select; import java.util.List; public interface UserDao { @Select("select * from t_user") List
findAll(); } -
配置
-
log4j.properties 日志配置同上面 xml 方式,略
-
mybatis-config.xml 配置如下
<?xml version="1.0" encoding="UTF-8"?>
-
-
测试
package cn.dao; import cn.pojo.User; 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.Test; import java.io.IOException; import java.io.InputStream; import java.util.List; public class UserDaoTest { @Test public void findAll() throws IOException { InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml"); SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession session = factory.openSession(); UserDao userDao = session.getMapper(UserDao.class); List
users = userDao.findAll(); for (User user : users) { System.out.println(user); } session.close(); inputStream.close(); } /** * 结果 * User(id=41, username=张三, birthday=Tue Feb 27 17:47:08 CST 2018, six=null, address=北京) * User(id=42, username=小明, birthday=Fri Mar 02 15:09:37 CST 2018, six=null, address=北京金燕龙) * User(id=43, username=小明, birthday=Sun Mar 04 11:34:34 CST 2018, six=null, address=北京金燕龙) * User(id=45, username=李四, birthday=Sun Mar 04 12:04:06 CST 2018, six=null, address=北京金燕龙) * User(id=46, username=王五, birthday=Wed Mar 07 17:37:26 CST 2018, six=null, address=北京) * User(id=48, username=赵六, birthday=Thu Mar 08 11:44:00 CST 2018, six=null, address=北京修正) */ }
3 增删改查
-
SQL脚本同上面的入门程序,略
-
#{}
与${}
的对比#{} ${} 一个占位符 一个sql串 自动进行java类型和jdbc类型转换 不进行jdbc类型转换 防止sql注入 无法防止sql注入 可以接收简单类型值或pojo属性值,
如果parameterType传输单个简单类型值,
#{}括号中可以是value或其它名称。可以接收简单类型值或pojo属性值,
如果parameterType传输单个简单类型值,
${}括号中只能是value- #{}相当于JDBC中的"?",而 "#{id}"表示该占位符待接收参数的名称为id。
- 拼接sql串时若想防止sql注入,可用mysql中的concat()函数。eg.
select * from t_customer where username like concat('%',#{value},'%')
3.1 xml方式
-
普通maven工程,结构图如下
-
整个项目基于上面入门程序的xml方式改造,因此下文仅展示不同部分的代码,相同部分略
-
实体类
package cn.pojo; import lombok.Data; import java.util.Date; @Data public class User { // 特意加个前缀user于列名不同,主要是用于测试resultMap映射 private Integer userId; private String userName; private String userAddress; private String userSex; private Date userBirthday; }
package cn.pojo; import lombok.Data; @Data public class FindPojo { User user; }
-
持久层
package cn.mapper; import cn.pojo.FindPojo; import cn.pojo.User; import java.util.List; /** * mapper层即持久层,与以前的dao层一样。 * 也可以写dao.UserDao,配置文件写成UserDao.xml * 但是mybatis提供的是mapper标签,因此用mapper居多, * 也可以写成dao层,两者完全一样 */ public interface UserMapper { void addUser(User user); void deleteUser(Integer id); void updateUser(User user); //包装类参数(注意xml中文件参数写法) List
findByPojo(FindPojo pojo); List findAll(); } -
配置
<?xml version="1.0" encoding="UTF-8"?>
INSERT INTO t_user (username, birthday, sex, address) VALUES (#{userName}, #{userBirthday}, #{userSex}, #{userAddress}) DELETE FROM t_user WHERE id = #{id} UPDATE t_user SET username = #{userName}, address = #{userAddress}, sex = #{userSex}, birthday = #{userBirthday} WHERE id = #{userId} -
测试
package cn.mapper; import cn.pojo.FindPojo; import cn.pojo.User; 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 java.io.IOException; import java.io.InputStream; import java.util.Date; import java.util.List; public class UserMapperTest { /** * 注:增删改提交事务的方法都放在 @After public void destory()方法中 * 别没看全以为不用提交事务! */ private InputStream inputStream; private SqlSession sqlSession; private UserMapper userMapper; @Before public void init() throws IOException { inputStream = Resources.getResourceAsStream("mybatis-config.xml"); SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream); sqlSession = factory.openSession(); userMapper = sqlSession.getMapper(UserMapper.class); } @After public void destory() throws IOException { //释放资源前提交事务,写在destory()内可以减少代码冗余,免得都写 sqlSession.commit(); //6.释放资源 sqlSession.close(); inputStream.close(); } @Test public void addUser() { User user = new User(); user.setUserName("张良"); user.setUserBirthday(new Date()); user.setUserSex("男"); user.setUserAddress("郑州"); userMapper.addUser(user); } @Test public void deleteUser() { userMapper.deleteUser(48); } @Test public void updateUser() { User user = new User(); user.setUserName("孙悟空"); user.setUserBirthday(new Date()); user.setUserSex("男"); user.setUserAddress("水帘洞"); user.setUserId(46); userMapper.updateUser(user); } @Test public void findByPojo() { FindPojo pojo = new FindPojo(); User user = new User(); user.setUserName("%小%"); pojo.setUser(user); List
users = userMapper.findByPojo(pojo); System.out.println(users); } @Test public void findAll() { List users = userMapper.findAll(); for (User user : users) { System.out.println(user); } } }
3.2 annotation方式
常用注解
序号 | 注解 | 说明 |
---|---|---|
1 | @Insert | 增 |
2 | @Delete | 删 |
3 | @Update | 改 |
4 | @Select | 查 |
5 | @Result | 实现结果集封装 |
6 | @Results | 可以与@Result一起使用,封装多个结果集 |
7 | @ResultMap | 实现引用@Results定义的封装 |
8 | @One | 实现一对一结果集封装 |
9 | @Many | 实现一对多结果集封装 |
10 | @SelectProvider | 实现动态SQL映射 |
11 | @CacheNamespace | 实现注解二级缓存的使用 |
复杂注解映射说明
- @Results注解代替的是
标签,该注解可以使用单个@Result,也可以使用@Result集合。- 样例:
@Results(@Result()) 或 @Results({@Result(),@Result()})
- 样例:
- @Result注解代替的是
和
标签,属性如下- id : 是否是主键字段
- property : 需要装配的属性名
- column : 数据库的列名
- one : 需要使用@One注解 ( @Result(one=@One) )
- many : 需要使用@Many注解 ( @Result(many = @Many) )
- @One注解(一对一)代替的是
标签,是多表查询的关键,在注解中用来指定子查询返回单一对象。- 其select属性指定用来多表查询的sqlMapper。
- 样例:
@Result(property="",column=" ",one=@One(select="")
- @Many注解(一对多)代替的是
标签,是多表查询的关键,在注解中用来指定子查询返回对象集合。- 注意:聚集元素用来处理“一对多”的关系。需要指定映射的Java实体类的属性,属性的javaType(一般为ArrayList)但是注解中可以不定义。
- 样例:
@Result(property="",column="",many=@Many(select=""))
代码
-
普通maven工程,结构图如下。
-
整个项目基于上面增删改查xml方式改造,因此下文仅展示不同部分的代码,相同部分略
-
持久层
package cn.mapper; import cn.pojo.FindPojo; import cn.pojo.User; import org.apache.ibatis.annotations.*; import java.util.List; /** * 个人建议:短sql用注解,太长的sql写在xml文件中,否则方法上一大堆太丑 * 注:定义了@Results(id = "userMap")之后,以后可以用@ResultMap("userMap")进行引用, * 例如下面的findByPojo方法可以不写SQL别名,添加注解@ResultMap("userMap")即可 * 定义@Results的方法(即findAll方法)无需使用,若是加上会编译报错!!! */ public interface UserMapper { @Insert("INSERT INTO t_user (username, birthday, sex, address)" + "VALUES (#{userName}, #{userBirthday}, #{userSex}, #{userAddress})") void addUser(User user); @Delete("DELETE FROM t_user WHERE id = #{id}") void deleteUser(Integer id); @Update("UPDATE t_user SET username = #{userName}, address = #{userAddress}," + "sex = #{userSex}, birthday = #{userBirthday} WHERE id = #{userId}") void updateUser(User user); @Select("SELECT ID AS userId, USERNAME AS userName, BIRTHDAY AS userBirthday," + "SEX AS userSex, ADDRESS AS userAddress FROM t_user WHERE USERNAME LIKE #{user.userName}") List
findByPojo(FindPojo pojo); // 包装类参数 @Select("SELECT * FROM t_user") @Results(id = "userMap", value = { @Result(property = "userId", column = "id"), @Result(property = "userName", column = "username"), @Result(property = "userAddress", column = "address"), @Result(property = "userSex", column = "sex"), @Result(property = "userBirthday", column = "birthday") } ) List findAll(); } -
配置 mybatis-config.xml
-
测试
- 同上面增删改查xml方式代码,略。
- 源码链接:blogs-mybatis