一.Mybatis介绍
MyBatis 本是apache的一个开源项目iBatis, 2013年11月迁移到Github。
MyBatis是一个优秀的持久层框架,它对jdbc的操作数据库的过程进行封装,使开发者只需要关注 SQL 本身。
Mybatis通过xml或注解的方式将要执行的各种statement(statement、preparedStatemnt、CallableStatement)配置起来,并通过java对象和statement中的sql进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射成java对象并返回。
二. Mybatis框架图
三. 入门项目实践
3.1 环境搭建
依照上述方法加入 junit单元测试包, mysql 或者 oracle 连接启动包 ,就可以了 。
3.2 加入配置文件
3.2.1 log4j.properties
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.err
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
### direct messages to file mylog.log ###
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=G:/Log4j_log/MyBatis_Base_log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
### set log levels - for more verbose logging change 'info' to 'debug' ###
log4j.rootLogger=debug, stdout
3.2.2 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>
<!-- 和spring整合后 environments配置将废除 -->
<environments default="development">
<environment id="development">
<!-- 使用jdbc事务管理 -->
<transactionManager type="JDBC"/>
<!-- 数据库连接池 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql://localhost:3306/mybatis_base?characterEncoding=utf-8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/tzq/batis/slqmap/User.xml"/>
</mappers>
</configuration>
3.3 创建pojo
pojo类作为mybatis进行sql映射使用,po类通常与数据库表对应。
数据库表
DROP TABLE IF EXISTS `orders`;
CREATE TABLE `orders` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL COMMENT '下单用户id',
`number` varchar(32) NOT NULL COMMENT '订单号',
`createtime` datetime NOT NULL COMMENT '创建订单时间',
`note` varchar(100) DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`id`),
KEY `FK_orders_1` (`user_id`),
CONSTRAINT `FK_orders_id` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
User.java
package com.tzq.batis.pojo;
import java.io.Serializable;
import java.util.Date;
public class User implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private Integer id;
private String username;// 用户姓名
private String sex;// 性别
private Date birthday;// 生日
private String address;// 地址
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "User [id=" + id + ", username=" + username + ", sex=" + sex
+ ", birthday=" + birthday + ", address=" + address + "]";
}
}
3.4 创建sql 映射文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace:命名空间,用于隔离sql,已经当 id 有重命名时 ,区分的标识 -->
<mapper namespace="test">
</mapper>
3.5 实现根据id查询用户
在user.xml中添加select标签,编写sql:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace:命名空间,用于隔离sql,已经当 id 有重命名时 ,区分的标识 -->
<mapper namespace="test">
<!-- id:statement的id 或者叫做sql的id-->
<!-- parameterType:声明输入参数的类型 -->
<!-- resultType:声明输出结果的类型,应该填写pojo的全路径 -->
<!-- #{}:输入参数的占位符,相当于jdbc的? -->
<select id="queryUserById" parameterType="int"
resultType="cn.itcast.mybatis.pojo.User">
SELECT * FROM `user` WHERE id = #{id}
</select>
</mapper>
3.6 测试程序:
public class MybatisTest {
private SqlSessionFactory sqlSessionFactory = null;
@Before
public void init() throws Exception {
// 1. 创建SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
// 2. 加载SqlMapConfig.xml配置文件
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
// 3. 创建SqlSessionFactory对象
this.sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
}
@Test
public void testQueryUserById() throws Exception {
// 4. 创建SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
// 5. 执行SqlSession对象执行查询,获取结果User
// 第一个参数是User.xml的statement的id,第二个参数是执行sql需要的参数;
Object user = sqlSession.selectOne("queryUserById", 1);
// 6. 打印结果
System.out.println(user);
// 7. 释放资源
sqlSession.close();
}
}
运行结果
更多sql 操作的 user.xml 代码
<!-- 写sql语句 , namespace 表示当id 在 另一个 xml map文件中有一样的名字是 , 可以通过它来判断是哪一个-->
<mapper namespace="com.tzq.batis.mapperdao.UserMapperDao">
<!-- #{}是 mybatis 里的通配符 resultType 代表 执行后的结果类型 parameterType 代表 下面通配符的属性类型-->
<!-- 通过ID查询-->
<select id="findByid" resultType="com.tzq.batis.pojo.User" parameterType="Integer">
select * from user where id = #{VALUE }
</select>
<!-- 根据用户名模糊查询数据
#{} 表示占位符 #{} == ? 转换为 sql 会 给 #{} 的外出加上 '' 单引号
${} 表示 字符串拼接 拼接不会加单引号
-->
<select id="findUserByName" resultType="com.tzq.batis.pojo.User" parameterType="String">
<!-- select * from user where username like "%"#{可以随便起名}"%" 与 下面语句一样的效果 -->
select * from user where username like '%${value}%'
</select>
<!-- 添加用户-->
<insert id="insertUser" parameterType="com.tzq.batis.pojo.User">
<selectKey resultType="integer" keyProperty="id" order="AFTER">
select LAST_INSERT_ID()
</selectKey>
insert into user
(username , sex) values (#{username},#{sex})
</insert>
<!-- 更新用户-->
<update id="updateUser" parameterType="com.tzq.batis.pojo.User">
update user SET username = #{username} where id = #{id}
</update>
<!-- 删除用户 -->
<delete id="deleteUser" parameterType="com.tzq.batis.pojo.User">
delete FROM user where id = #{id}
</delete>
</mapper>
对应的java代码
public class TestUserMybatis {
/*
获取session
*/
public SqlSession getSqlSession() throws IOException {
//加载核心配置文件
String resource = "sqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
//创建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//创建sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
return sqlSession;
}
/*
通过了ID 查询数据
*/
@Test
public void testSelect() throws IOException {
SqlSession sqlSession = getSqlSession();
//执行sql语句 ,表示查询一个
User user = sqlSession.selectOne("com.tzq.batis.mapperdao.UserMapperDao.findByid", 1);
System.out.println(user.toString());
sqlSession.close();
}
/*
根据用户名模糊查询数据
*/
@Test
public void testFindDimName() throws IOException {
SqlSession sqlSession = getSqlSession();
//执行sql语句
List<User> list = sqlSession.selectList("test.findUserByName", "五");
for (User user : list) {
System.out.println(user.toString());
}
sqlSession.close();
}
/*
添加用户
*/
@Test
public void testInsertUser() throws IOException {
SqlSession sqlSession = getSqlSession();
//执行sql语句
User user = new User();
user.setUsername("李四");
user.setSex("男");
int insert = sqlSession.insert("test.insertUser", user);
sqlSession.commit();
sqlSession.close();
System.out.println(user.getId());
}
/*
修改用户
*/
@Test
public void testUpdateUserById() throws IOException {
SqlSession sqlSession = getSqlSession();
//执行sql语句
User user = new User();
user.setId(29);
user.setUsername("张三");
sqlSession.update("updateUser", user);
sqlSession.commit();
sqlSession.close();
}
/*
删除用户
*/
@Test
public void testDeleteUserById() throws IOException {
SqlSession sqlSession = getSqlSession();
//执行sql语句
User user = new User();
user.setId(29);
sqlSession.update("deleteUser", user);
sqlSession.commit();
sqlSession.close();
}
}
3.7 小结
3.7.1 #{}和${}
#{}表示一个占位符号,通过#{}可以实现preparedStatement向占位符中设置值,自动进行java类型和jdbc类型转换。#{}可以有效防止sql注入。 #{}可以接收简单类型值或pojo属性值。 如果parameterType传输单个简单类型值,#{}括号中可以是value或其它名称。
表示拼接sql串,通过 {}可以将parameterType 传入的内容拼接在sql中且不进行jdbc类型转换, 可以接收简单类型值或pojo属性值,如果parameterType传输单个简单类型值, {}括号中只能是value。
3.7.2. parameterType和resultType
parameterType:指定输入参数类型,mybatis通过ognl从输入对象中获取参数值拼接在sql中。
resultType:指定输出结果类型,mybatis将sql查询结果的一行记录数据映射为resultType指定类型的对象。如果有多条数据,则分别进行映射,并把对象放到容器List中
3.7.3. selectOne和selectList
selectOne查询一条记录,如果使用selectOne查询多条记录则抛出异常:
org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 3
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectOne(DefaultSqlSession.java:70)
selectList可以查询一条或多条记录。
3.8 Dao开发
3.8.1传统Dao的开发
Dao接口
public interface UserDao {
public User selectUserById(Integer id);
}
Dao接口的实现类
public class UserDaoImpl implements UserDao {
private SqlSessionFactory sqlSessionFactory;
public UserDaoImpl(SqlSessionFactory sqlSessionFactory) {
this.sqlSessionFactory = sqlSessionFactory;
}
@Override
public User selectUserById(Integer id) {
SqlSession sqlSession = sqlSessionFactory.openSession();
User user = sqlSession.selectOne("findByid", id);
return user;
}
}
junit 测试类
public class TestUserDao {
private SqlSessionFactory sqlSessionFactory;
@Before
public void before() throws Exception {
//加载核心配置文件
String resource = "sqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
//创建SqlSessionFactory
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
/**
* Method: TestDao()
*/
@Test
public void testTestDao() throws Exception {
UserDao dao = new UserDaoImpl(sqlSessionFactory);
User user = dao.selectUserById(10);
System.out.println(user.getUsername());
}
}
3.9 重量级嘉宾登场 —— Mapper动态代理方式
3.9.1 开发规范
Mapper接口开发方法只需要程序员编写Mapper接口(相当于Dao接口),由Mybatis框架根据接口定义创建接口的动态代理对象,代理对象的方法体同上边Dao接口实现类方法。
#代理的接口代码:
public interface UserMapperDao {
/**
* Mapper 动态代理方法
* 只要实现了下面的四个原则 , 就不用写daoImpl 了
* Created by MyPC on 2017/8/19.
*/
/*
四个原则
1.接口的方法名与 Mapper.xml 中的 id 名 一样
2.返回值类型与Mapper.xml文件中返回值类型一致
3.方法的 输入参数 类型要与 Mapper.xml 的一致
4.Mapper.xml命名空间绑定这个接口全路径名
*/
public User findByid(Integer id);
}
#junit测试类
public class TestUserMapperDao {
@Test
public void testSelectUserById() throws IOException {
//加载核心配置文件
String resource = "sqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
//创建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//创建sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapperDao dao = sqlSession.getMapper(UserMapperDao.class);
User user = dao.findByid(10);
System.out.println(user.toString());
}
}
官网推荐使用 动态代理的方式 , 当然您可以根据自己的需求去写。