MyBatis
JavaEE命名规范
- 项目名:没有要求,尽量不用中文
- 包:公司域名倒写com.zephon
- 持久层:dao,persist,mapper
- 实体:entity,model,bean,javabean,pojo
- 业务逻辑:service,biz
- 控制器:controller,servlet,action,web
- 过滤器:filter
- 异常:exception
- 监听器:listener
- 注释:
- 类上和方法上使用文档注释/** */
- 在方法里面使用/* */或//
- 类:使用大驼峰(首字母大写,后面每个单词首字母大写)
- 方法,属性:小驼峰(首字母小写,后面每个单词首字母大写)
框架介绍
框架是什么
框架:软件的半成品,为解决问题制定的一套约束,在提供功能基础上进行扩充
框架中一些不能被封装的代码(变量),需要使用框架者自己新建一个xml文件,在文件中添加变量内容,因此,几乎所有框架,都需要建立特定位置和特定名称的配置文件,这使用的就是xml解析技术和反射技术。
常用概念:
类库:提供的类没有封装一定的逻辑,就像写作文时用的名言警句
框架:有一些约束,就像是模板,只用填空就可以写作文
二者表现形式都是引入jar包
MyBatis简介
- Mybatis,开源免费框架,原名叫iBatis
- 作用:数据访问层框架,底层是对JDBC的封装
- 优点之一是:使用MyBatis时不需要编写实现类,只需要写需要执行的sql命令
MyBatis使用
- 导入jar包
- 在src下新建全局配置文件,没有名称和地址要求
- 在全局配置文件中引入DTD或schema
1 |
|
新建包com.zephon.mapper,在里面新建xml文件,名字建议为实体类Mapper.xml
文件作用:编写需要执行的SQL命令,可以将xml文件理解成实现类(dao层)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!-- namespace:实现类的全路径(包名+类名) -->
<mapper namespace="com.zephon.mapper">
<!-- id:方法名
parameterType:参数类型
resultType:返回值类型
如果方法返回值是list,则需要在resultType中写List泛型,因为mybatis是对jdbc封装,是一行一行读取数据
如果加了parameterType=“”,“”中没内容就会报错
-->
<select id="selAll" resultType="com.zephon.pojo.User">
select * from ta_user
</select>
</mapper>测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14public class Test {
public static void main(String[] args) throws IOException {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
//使用工厂设计模式
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
//生产SqlSession
SqlSession sqlSession = factory.openSession();
List<User> list = sqlSession.selectList("com.zephon.mapper.selAll");
for(User u:list){
System.out.println(u);
}
}
}
环境搭建详解
- < transactionManager />type属性可取值
- JDBC,事务管理使用JDBC原生事务管理方式
- MANAGED,将事务管理转交给其它窗口,相当于原生JDBC事务setAutoMapping(false);
- < dataSource /> type属性值
- POOLED 使用数据库连接池
- UNPOOLED 不使用数据库连接池,和直接使用JDBC一样
- JNDI Java命名目录接口技术
数据库连接池
在内存中开辟一块空间,存放多个数据库连接对象
JDBC Tomcat Pool,直接由Tomcat产生数据库连接池
状态
active :当前连接对象被应用程序使用中
Idle:空闲状态;等待应用程序使用
使用数据库连接池的目的:在高频率访问数据库时,使用数据库连接池可以降低服务器系统压力,提升程序运行效率
小型项目不适用数据库连接池
实现JDBC tomcat Pool的步骤
在web项目的META-INF中存放context.xml,写数据库连接池相关属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<Context>
<Resource
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/Mysql"
username="root"
password=""
maxActive="50"
maxIdle="20"
name="test"
auth="Container"
maxWait="10000"
type="javax.sql.DataSource"
/>
</Context>把项目发布到tomcat中,数据库连接池就产生了
可以在java中使用jndi获取数据库连接池中的对象
Context:上下文接口,context.xml文件对象类型
代码:
1
2
3Context cxt = new InitialContext();
DataSource ds = cxt.lookup("java:com/env/test");
Connection conn = ds.getConnection();当关闭连接对象时,是将连接对象归还给数据库连接池,并将状态改为Idle,而非真正关闭
三种查询方式
1 | <select id="selAll" resultType="com.zephon.pojo.User"> |
selectList() 返回值为List< resultType属性控制 >
适用于查询结果都需要遍历的需求
1
2
3
4List<User> list = sqlSession.selectList("com.zephon.mapper.selAll");
for(User u:list){
System.out.println(u.getUid()+u.getUname()+u.getPwd());
}selectOne()返回Object
适用于返回结果只是变量或一行数据时
1
2User user=sqlSession.selectOne("com.zephon.mapper.selById");
System.out.println(user.getUname());selectMap()返回Map
适用于需要在查询结果中快速通过某列的值取到这行数据的需求
Map<key,resultType控制>
1
2Map<Object, Object> map = sqlSession.selectMap("com.zephon.mapper.selById", "uname");
System.out.println(map);
Log4J
由apache推出的开源免费日志处理的类库
为什么需要日志
- 在项目中编写System.out.prinln();输出到控制台,当项目发布到tomcat后,没有控制台(在命令行能看见),不容易观察一些输出结果
- log4J作用:不仅能把内容输出到控制台,不能把内容输出到文件中,便于观察结果
使用步骤
- 导入jar包
- 在src下新建log4j.properties(路径和名称固定)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16### 设置###
DEBUG,CONSOLE,LOGFILE =
#log4j.rootLogger = DEBUG,CONSOLE
### 输出信息到控制抬 ###
org.apache.log4j.ConsoleAppender =
System.out =
org.apache.log4j.PatternLayout =
[%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n =
### 输出信息到文件 ###
org.apache.log4j.FileAppender =
/home/zephon/JetB rains/logs/my.log =
true; =
org.apache.log4j.PatternLayout =
[%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n =1
2Logger logger = Logger.getLogger(TestLog4j.class);
logger.debug("testLog4j");log4j输出级别
fatal(致命错误)>error(错误)>warm(警告)>info(普通信息)>debug(调试信息)
pattern中常用表达式
%C:包名+类名
%d{YYYY-MM-dd HH:mm:ss}:时间
%L:行号
%m:信息
%n:换行
< settings >标签
在mybatis全局配置文件中通过< settings >标签控制mybatis全局开关
在mybatis-config.xml中开启log4j
1
2
3<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>log4j中可以输出指定内容的日志(控制指定内容)
- 命名空间级别(包级别)< mapper >namespace属性中除了最后一个类名,需要在log4j.propeties中先在总体级别调成Error,然后log4j.logger.com.a.b=debug
- 类级别
- 方法级别
parameterType属性
在xxxMapper.xml中< select >< delete >等标签的parameterType可以控制参数类型
在SqlSession的selectXxx()中第二个参数传递中传递参数以实现查询特定数据
在Mapper.xml中可以通过#{}获取参数
- 使用索引,从arg0开始,#{arg0}表示第一个参数
- 使用#{param1}表示第一个参数
- 如果只有一个参数(基本类型或String),mybatis对#{}里面的内容没有要求只要写内容即可
1
2
3<select id="selById" parameterType="int" resultType="com.zephon.pojo.User">
select * from ta_user where uid=#{0}
</select>1
2User user=sqlSession.selectOne("com.zephon.dao.selById",2);
System.out.println(user.getUname());- 如果参数是对象,使用#{属性名}
- 如果参数是map,写成#{key}
1
2
3<select id="selById" parameterType="map" resultType="com.zephon.pojo.User">
select * from ta_user where uid=#{uid} and uname=#{uname}
</select>1
2
3
4
5
6//传递多个参数
Map<String,Object> map = new HashMap<>();
map.put("uid",3);
map.put("uname","小王");
User user=sqlSession.selectOne("com.zephon.dao.selById",map);
System.out.println(user.getUname());#{}或${}区别
- #{}获取参数的内容支持索引获取,param1获取指定位置参数,并在SQL语句中使用?占位符
- \({}字符串拼接,不使用?,默认找\){内容}内容的get/set方法,如果写数字,就是一个数字
1
2
3<select id="selById" parameterType="int" resultType="com.zephon.pojo.User">
select * from ta_user where uid=${uid}
</select>1
2
3
4User u = new User();
u.setUid(2);
User user=sqlSession.selectOne("com.zephon.dao.selById",u);
System.out.println(user.getUname());
使用mybatis实现mysql分页
注:?不允许在关键字前后进行数学运算,所以需要在代码中计算完成再传入mapper.xml
1 | <select id="selPage" parameterType="map" resultType="com.zephon.pojo.User"> |
1 | //分页 |
别名
系统内置别名:把类型全小写(Map->map,List->list,Integer->int)
给某个类起别名
1
2
3<typeAliases>
<typeAlias type="com.zephon.pojo.User" alias="U"/>
</typeAliases>1
2
3<select id="selAll" resultType="U">
select * from ta_user
</select>直接给某个包下所有类起别名,别名为类名
1 | <typeAliases> |
1 | <select id="selAll" resultType="User"> |
MyBatis实现新增
概念
- 功能:从应用程序角度出发,软件具有哪些功能
- 业务:完成功能时的逻辑
- 事务:从数据库角度出发,完成业务时需要执行的SQL集合,统称一个事务
- 事务回滚:如果在一个事务中某个SQL执行失败,希望回归到事务的原点,保证数据库数据的完整性
在mybatis中默认是关闭了JDBC的自动提交功能的
- 每一个SqlSession默认都是不自动提交事务
- session.commit()提交事务
- openSession(true);设置自动提交setAutoCommit(true);
mybatis底层是对JDBC的封装
- JDBC中executeUpdate()执行新增,删除,修改的SQL返回值int类型,表示受影响的行数
- mybatis中< insert >< delete > < update >标签没有resultType属性,默认返回值为int类型
在openSession()时,mybatis会创建SqlSession时同时创建一个Transation对象 ,同时autoCommit都为false]
- 如果出现异常,应该用session.rollback()回滚
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25public class Test {
public static void main(String[] args) throws IOException {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
//使用工厂设计模式
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
//生产SqlSession
SqlSession sqlSession = factory.openSession();
User u = new User();
u.setUname("新增");
u.setPwd("zdf");
try{
int index = sqlSession.insert("insUser",u);
if(index>0){
System.out.println("成功");
}else{
System.out.println("失败 ");
}
}catch (Exception e){
sqlSession.rollback();
}
sqlSession.commit();
sqlSession.close();
}
}
Mybatis接口绑定方案及多参数传递
作用:实现创建一个接口后把mapper.xml由mybatis生成接口的实现类,通过调用接口对象就可以获取mapper.xml中编写的sql
实现步骤
- 创建一个接口
- 接口包名和接口名与mapper.xml中< mappers>namespace相同
- 接口中方法名和mapper.xml标签的id属性相同
- 在mybatis-config.xml中使用< package>进行扫描接口和mapper.xml
- 创建一个接口
代码实现
在mybatis-config.xml中< mappers>下使用< package>
1
2
3<mappers>
<package name="com.zephon.mapper"/>
</mappers>在com.xxx.mapper下新建接口
1
2
3public interface UserMapper {
List<User> selAll();
}在com.xxx.mapper新建一个UserMapper.xml
- 其中namespace必须与接口全限定路径(包名+类名)相同
- id值必须与接口中方法名相同
- 如果接口中方法多个参数,可以省略parameterType
1
2
3
4
5<mapper namespace="com.zephon.mapper.UserMapper">
<select id="selAll" resultType="User">
select * from ta_user
</select>
</mapper>多参数实现办法
在接口中声明方法
1
2
3
4
5
6
7
8//List<User> selByUidName(int uid,String uname);
/**
* 没有注解时,对于基本类型,xml中使用只能用param1或arg0
* 使用注解后,mybatis把参数转换为map了,其中@Param("key")
* 参数内容就是map的value
*/
List<User> selByUidName(int uid, String uname) ;在mapper.xml中添加
1
2
3
4<!-- 当多参数时,不需要写parameterType -->
<select id="selByUidName" resultType="User">
select * from ta_user where uid=#{param1} and uname=#{param2}
</select>
Test
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24public class Test {
public static void main(String[] args) throws IOException {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
SqlSession session = factory.openSession();
/**
* 接口,为什么能实例化?
* 需要给接口一个实例化对象
* 使用的是JDK的动态代理设计模式(面向接口的代理设计模式(必须有接口))
*/
UserMapper userMapper = session.getMapper(UserMapper.class);
// List<User> list = userMapper.selAll();
// for (User u : list) {
// System.out.println(u.getUname()+"-"+u.getPwd());
// }
List<User> l = userMapper.selByUidName(1, "小明");
for (User u : l) {
System.out.println(u.getUname()+"-"+u.getPwd());
}
session.close();
}
}
动态SQL
根据不同的条件需要执行不同的SQL命令,称为动态SQL
Mybatis中动态SQL就是在mapper.xml中添加逻辑判断等
if的使用
1
2
3
4
5
6
7
8
9
10<select id="selByUnamePwd" resultType="User">
select * from ta_user where 1=1
<!-- OGNL表达式,直接写key或对象的属性,不需要添加任何特殊符号 -->
<if test="uname!=null and uname!=''">
and uname=#{uname}
</if>
<if test="pwd!=null and pwd!=''">
and pwd=#{pwd}
</if>
</select>where
- 当编写where标签时,如果内容中第一个是and,会去掉第一个and
- 如果where中有内容会生成where关键字,如果没有内容不生成where关键字
1
2
3
4
5
6
7
8
9
10
11<select id="selByUnamePwd" resultType="User">
select * from ta_user
<where><!-- 其实就是代替了where 1==1,但是效率有提高-->
<if test="uname!=null and uname!=''">
and uname=#{uname}
</if>
<if test="pwd!=null and pwd!=''">
and pwd=#{pwd}
</if>
</where>
</select>choose、when、otherwise
- 只要有一个成立,其它都不执行
1
2
3
4
5
6
7
8
9
10
11
12
13<select id="selByUnamePwd" resultType="User">
select * from ta_user
<where>
<choose>
<when test="uname!=null and uname!=''">
and uname=#{uname}
</when>
<when test="pwd!=null and pwd!=''">
and pwd=#{pwd}
</when>
</choose>
</where>
</select>set用作修改SQL中set从句
作用:去掉最后一个逗号,如果set里面有内容,生成关键字,否则不生成
示例:
- uid=#{uid}目的是防止< set >中没有内容,mybatis不生成set关键字,如果修改中没有set从句,会有SQL语法错误
1
2
3
4
5
6
7
8
9
10
11
12
13<update id="upd" parameterType="User">
update ta_user
<set>
uid=#{uid},
<if test="uname!=null and uname!=''">
uname=#{uname},
</if>
<if test="pwd!=null and pwd!=''">
pwd=#{pwd},
</if>
</set>
where uid=#{uid}
</update>
trim
- prefix在前面添加内容
- prefixOverrides去掉前面内容
- suffix在后面添加内容
- suffixOverrides去掉后面的内容
- 执行顺序:先去掉内容,再添加内容
1
2
3
4
5
6
7<!--- 相当于使用<set> -->
<update id="upd" parameterType="User">
update ta_user
<trim prefix="set" suffixOverrides="," >
a=a,
</trim>
</update>bind
作用:给参数重新赋值
使用场景:
- 模糊查询
- 在原内容前或后添加内容
示例
1
2
3
4<select id="">
<!-- 模糊查询 -->
<bind name="uname" value="'%'+uname+'%'"/>
</select>
foreach
循环参数内容,还具备在内容的前后添加内容、添加分隔符功能
适用场景:在in查询中常用,批量新增(mybatis中foreach效率比较低)
1
2
3
4
5
6
7
8
9
10
11
12<!-- collection:要遍历的集合
item:迭代变量,#{迭代变量名}获取内容
open:循环后左侧添加的内容
close:循环后右侧添加的内容
separator:每次循环,元素之间的分隔符
-->
<select id="selIn" parameterType="list" resultType="User">
select * from ta_user where uid in
<foreach collection="list" item="i" open="(" close=")" separator=",">
#{i}
</foreach>
</select>
sql和include
某些SQL片段如果希望复用,可以使用< sql>定义这个片段
1
2
3<sql id="mysql">
uid,uname,pwd
</sql>在< select>或< delete>或< update>或< insert>中使用< include>引用
1
2
3
4<select id="">
select <include refid="mysql"></include>
from ta_user
</select>