Mybatis入门
MyBatis是一个ORM的数据库持久化框架。它以映射sql语句orm方式来数据库持久化操作。
一、导包
二、准备相应的表、domain、dao、测试
三、简单实现
3.1 步骤分析
①获取SqlSessionFactory
1 我们需要准备一个核心的Mybatis-config.xml文件
2 拿到SqlSessionFactory之前需要读取核心的xml配置文件
3 需要构造者(SqlSessionFactoryBuilder)来创建它
②映射文件准备
③通过SqlSessionFactory获取SqlSession执行映射SQL
3.2 核心文件
核心配置文件MyBatis-Config.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>
<!--引入jdbc.propeties文件-->
<properties resource="jdbc.properties" />
<!--配置包-->
<typeAliases>
<package name="cn.itsource.domain"></package>
<package name="cn.itsource.query"></package>
</typeAliases>
<!-- 可以有多个环境,default:默认使用哪一个环境(必需对应一个环境的id) -->
<environments default="development">
<!-- 一个环境 id:为这个环境取唯一一个id名称 -->
<environment id="development">
<!-- 事务管理 type:JDBC(支持事务)/MANAGED(什么都不做) -->
<transactionManager type="JDBC" />
<!-- 数据源, 连接池 type(POOLED):MyBatis自带的连接池 -->
<dataSource type="POOLED">
<!-- 连接数据库的参数 -->
<property name="driver" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</dataSource>
</environment>
</environments>
<!-- 这个mappers代表的是相应的ORM映射文件 -->
<mappers>
<mapper resource="cn/itsource/domain/UserMapper.xml" />
</mappers>
</configuration>
3.3 映射文件
①我们的映射文件(就是我们的mapper文件)一般情况下是和它对应的domain实体类在同一个层级
② 这个映射文件的名称一般叫做 XxxMapper.xml (Xxx代表的是实体类名称)
③ 除了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的主要功能就是写sql
mapper:根 namespace:命令空间
namespace的值,规则的:映射文件XxxMapper.xml所在的包+domain类名+Mapper
-->
<mapper namespace="cn.itsource.dao.IUserDao">
<!--
select : 这里面写查询语句
id:用来确定这条sql语句的唯一
parameterType : 传入的参数类型 long:大Long _long:小long (具体的对应请参见文档)
resultType : 结果类型(第一条数据返回的对象类型) 自己的对象一定是全限定类名
-->
<select id="queryAll" resultType="User">
select * from t_user
</select>
<select id="queryOne" parameterType="long" resultType="User">
select * from t_user where id=#{id}
</select>
<!--添加-->
<insert id="save" parameterType="User">
insert into t_user(name) values(#{name})
</insert>
<!--修改-->
<update id="update" parameterType="User">
update t_user set name=#{name} where id=#{id}
</update>
<!--删除-->
<delete id="delete" parameterType="long">
delete from t_user where id=#{id}
</delete>
<!--批量删除-->
<delete id="deleteBatch" parameterType="list">
delete from t_user where id in
<foreach collection="list" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</delete>
<!--批量添加-->
<insert id="saveBatch" parameterType="list">
insert into t_user(name) VALUES
<foreach collection="list" item="user" separator=",">
(#{user.name})
</foreach>
</insert>
<!--批量修改-->
<update id="updateBatch" parameterType="list">
UPDATE t_user SET name="1111" WHERE id IN
<foreach collection="list" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</update>
<!--高级查询-->
<select id="queryList" parameterType="userQuery" resultType="User">
select * from t_user
<where>
<if test="name!=null">
and name like #{name}
</if>
<if test="age!=null">
and age = #{age}
</if>
</where>
</select>
</mapper>
3.4 实现
Dao层:
public interface IUserDao {
//查询所有数据
List<User> queryAll() throws IOException;
//根据ID查数据
User queryOne(Long id);
//添加方法
void save(User user);
//修改方法
void update(User user);
//删除方法
void delete(Long id);
//批量删除
void deleteBatch(List ids);
//批量添加
void saveBatch(List user);
//批量修改
void updateBatch(List ids);
//高级查询
List<User> queryList(UserQuery userQuery);
}
DaoImpl实现类:
public class UserDaoImpl implements IUserDao {
@Override
public List<User> queryAll() throws IOException {
//1 创建SqlSessionFactory
Reader reader = Resources.getResourceAsReader("MyBatis-Config.xml");
//2 获取sqlSession做操作
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
SqlSession sqlSession = sessionFactory.openSession();
//3 表示调用那句sql
List<User> users = sqlSession.selectList("cn.itsource.dao.IUserDao.queryAll");
return users;
}
@Override
public User queryOne(Long id) {
User user = MyBatisUtil.INSTANCE.getSqlSession().selectOne("cn.itsource.dao.IUserDao.queryOne", id);
return user;
}
@Override
public void save(User user) {
SqlSession sqlSession = MyBatisUtil.INSTANCE.getSqlSession();
int insert = sqlSession.insert("cn.itsource.dao.IUserDao.save", user);
sqlSession.commit();
}
@Override
public void update(User user) {
SqlSession sqlSession = MyBatisUtil.INSTANCE.getSqlSession();
int insert = sqlSession.update("cn.itsource.dao.IUserDao.update", user);
sqlSession.commit();
}
@Override
public void delete(Long id) {
SqlSession sqlSession = MyBatisUtil.INSTANCE.getSqlSession();
int insert = sqlSession.delete("cn.itsource.dao.IUserDao.delete", id);
sqlSession.commit();
}
//批量删除
@Override
public void deleteBatch(List ids) {
SqlSession sqlSession = MyBatisUtil.INSTANCE.getSqlSession();
int insert = sqlSession.delete("cn.itsource.dao.IUserDao.deleteBatch", ids);
sqlSession.commit();
}
//批量添加
public void saveBatch(List user){
SqlSession sqlSession = MyBatisUtil.INSTANCE.getSqlSession();
int insert = sqlSession.insert("cn.itsource.dao.IUserDao.saveBatch", user);
sqlSession.commit();
}
@Override
public void updateBatch(List ids) {
SqlSession sqlSession = MyBatisUtil.INSTANCE.getSqlSession();
int insert = sqlSession.update("cn.itsource.dao.IUserDao.updateBatch", ids);
sqlSession.commit();
}
@Override
public List<User> queryList(UserQuery userQuery) {
SqlSession sqlSession = MyBatisUtil.INSTANCE.getSqlSession();
List<User> users = sqlSession.selectList("cn.itsource.dao.IUserDao.queryList", userQuery);
return users;
}
}
Test测试类
public class mybatisTest {
@Test
public void testQueryAll() throws Exception{
List<User> users = new UserDaoImpl().queryAll();
users.forEach(e-> System.out.println(e));
}
@Test
public void testQueryOne() throws Exception{
User user = new UserDaoImpl().queryOne(4L);
System.out.println(user);
}
@Test
public void testsave() throws Exception{
User user = new User();
user.setName("暗示大家哦");
new UserDaoImpl().save(user);
}
@Test
public void testUpdate() throws Exception{
User user = new User();
user.setId(2L);
user.setName("我被改变了");
new UserDaoImpl().update(user);
}
@Test
public void testdelete() throws Exception{
new UserDaoImpl().delete(3L);
}
//批量删除
@Test
public void testdeleteBatch() throws Exception{
//用一个list来装我们要删除数据的id
List<Long> ids = Arrays.asList(5L);
new UserDaoImpl().deleteBatch(ids);
}
//批量添加
@Test
public void testSaveBatch() throws Exception{
List<User> users = Arrays.asList(new User("飞机"),new User("大炮"),new User("火箭"));
new UserDaoImpl().saveBatch(users);
}
//批量修改
@Test
public void testUpdateBatch() throws Exception{
List<Long> ids = Arrays.asList(9L,10L,11L);
new UserDaoImpl().updateBatch(ids);
}
//高级查询
@Test
public void testQueryList() throws Exception{
UserQuery query = new UserQuery();
//query.setName("%蔡徐坤%");
query.setAge(100);
List<User> users = new UserDaoImpl().queryList(query);
users.forEach(e-> System.out.println(e));
}
}
四、抽取Mybatis CRUD-工具类
4.1抽取理论
SqlSessionFactoryBuilder
建造者模式:我们最后拿到的这个对象是非常复杂的. 用这个建造者就它先为我们把这些复杂的代码完成. 这个类可以被实例化,使用和丢弃。一旦创建了SqlSessionFactory后,这个类对象就不需要存在了。因此SqlSessionFactoryBuilder实例的最佳范围是方法范围(也就是本地方法变量)。
SqlSessionFactory
一旦被创建,SqlSessionFactory应该在你的应用执行期间都存在。没有理由来处理或重新创建它。使用SqlSessionFactory的最佳实践是在应用运行期间不要重复创建多次。这样的操作将被视为是非常糟糕的。因此SqlSessionFactory的最佳范围是应用范围。有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。然而这两种方法都不认为是最佳实践。
SqlSession
每个线程都应该有它自己的SqlSession实例。SqlSession的实例不能被共享,也是线程不安全的。因此最佳的范围是请求或方法范围。绝对不能将SqlSession实例的引用放在一个类的静态字段甚至是实例字段中。也绝不能将SqlSession实例的引用放在任何类型的管理范围中,比如Serlvet架构中的HttpSession。如果你现在正用任意的Web框架,要考虑SqlSession放在一个和HTTP请求对象相似的范围内。换句话说,基于收到的HTTP请求,你可以打开了一个SqlSession,然后返回响应,就可以关闭它了。关闭Session很重要,
4.2 抽取
public enum MyBatisUtil {
INSTANCE;
private static SqlSessionFactory sessionFactory;
static {
Reader reader =null;
try {
reader = Resources.getResourceAsReader("MyBatis-Config.xml");
sessionFactory = new SqlSessionFactoryBuilder().build(reader);
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("配置文件出问题了"+e.getMessage());
}
}
public SqlSession getSqlSession(){
return sessionFactory.openSession();
}
}
五、Mybtis使用细节
5.1 添加时拿到返回的主键
<!--
parameterType:需要传入我们的对象
useGeneratedKeys: 是否需要主键
keyColumn:主键所在的列
keyProperty:对象中的属性(代表主键的那个属性)
-->
<insert id="save" parameterType="cn.itsource.domain.Product"
useGeneratedKeys="true"
keyColumn="id"
keyProperty="id"
>
insert into product (productName,dir_id,salePrice,supplier,brand,cutoff,costPrice)
values (#{productName},#{dir_id},#{salePrice},#{supplier},#{brand},#{cutoff},#{costPrice})
</insert>
5.2 查看MyBatis运行日志
在使用MyBatis的很多时候,我们需要把日志打印出来,帮助我们进行分析与排错.
要在项目中打开日志,大家在资源文件根目录下创建一个log4j.properties的文件,并把下面的代码拷备到里面。
# 日志输出级别 输出到控制
log4j.rootLogger=ERROR, stdout
#log4j.rootLogger=NONE
log4j.logger.cn.itsource=TRACE
# 输出到控制台的配置信息
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
# 输出到控制台的 格式类
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n
5.3 在MyBatis中别名
别名就是为类取得小名,以后可以使用小名代码真实名称,在Mybatis中,别名分为内置别名和自定义别名。
5.3.1 内置别名
5.3.2 自定义别名
<typeAliases>
<!-- 单个配置:练习 -->
<typeAlias type="cn.xxxx.mybatis.crud.User" alias="User" />
<!-- 包的配置:项目,添加了包之后,类名就是别名 -->
<package name="cn.xxxx.mybatis.crud" />
</typeAliases>
5.4 #与$区别
5.4.1 #{OGNL表达式}
MyBatis会把这个表达式使用?(占位符)替换,作为一个sql参数使用:推荐使用
比如:
定义SQL: select * from t_user where name = #{name}
最终SQL: select * from t_user where name = ?
5.4.2 ${OGNL表达式}
MyBatis会把这个表达式的值替换到sql中,作为sql的组成部分; 把获取到值直接拼接SQL,该方式主要用于程序拼接SQL;
比如:
定义SQL: select id,name,password from t_user where name =
n
a
m
e
a
n
d
p
a
s
s
w
o
r
d
=
{name} and password=
nameandpassword={password}
最终SQL: select id,name,password from t_user where name=“test” or “1=1” and password=“test” 出现sql注入
除了要拼接sql结构体要用$(是直接拼接sql),其他都用#,因为用#会替换为?,最终通过prepareStament来设置参数,不会有sql注入问提。
六、常用的动态SQL
MyBatis 的一个强大的特性之一通常是它的动态 SQL 能力。如果你有使用 JDBC 或其他相似框架的经验,你就明白条件地串联 SQL 字符串在一起是多么的痛苦,确保不能忘了空格或在列表的最后省略逗号。动态 SQL 可以彻底处理这种痛苦。
具体代码在上面已经体现。