一.Mybatis简介
1.MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。
iBATIS一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。iBATIS提供的持久层框架包括SQL Maps和Data Access Objects(DAOs)。
2.功能架构:
(1)API接口层:提供给外部使用的接口API,开发人员通过这些本地API来操纵数据库。接口层一接收到调用请求就会调用数据处理层来完成具体的数据处理。
(2)数据处理层:负责具体的SQL查找、SQL解析、SQL执行和执行结果映射处理等。它主要的目的是根据调用的请求完成一次数据库操作。
(3)基础支撑层:负责最基础的功能支撑,包括连接管理、事务管理、配置加载和缓存处理,这些都是共用的东西,将他们抽取出来作为最基础的组件。为上层的数据处理层提供最基础的支撑。
二.项目实例
需要导入的包:ojdbc6.jar(访问数据库必备的包)、mybatis-3.4.6.jar(Mybatis框架包)、log4j-1.2.17.jar(用于控制台调试的工具包)
需要的文件:log4j.properties(调试模板)
配置Mybatis的总配置文件:
<?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>
<properties resource="conf/db.properties"></properties><!-- 加载属性文件,也可以直接定义属性值 -->
<typeAliases><!-- 别名类型,这样写默认以该包下类名为别名 -->
<package name="com.hanqi.model"/>
<!-- <typeAlias type="com.hanqi.model.House" alias="hhh"/>按照自己起的别名 -->
</typeAliases>
<environments default="development"><!-- 配置mybatis的运行环境 ,一般环境有3个,设置默认为开发环境-->
<environment id="development"><!-- 开发环境 -->
<!--
配置事务管理器, 有两种方式:
1, JDBC
2, MANAGED(拖管, 一般的商业服务器才会有这个功能, 比如JBOSS,Weblogic, tomcat没有)
-->
<transactionManager type="JDBC"/><!-- 配置事物管理器类型 -->
<!--
数据源
type参数有三种: UNPOOLED, POOLED, JNDI
UNPOOLED: 没有连接池, 使用最普通的连接, 每次调用数据库mybatis都会创建一个新的连接, 用完之后关闭, 适合小并发项目(并发: 用户访问量的意思)
POOLED: 使用连接池, 创建若干个连接, 用完之后放回连接池
JNDI: 使用应用服务器配置JNDI数据源获取数据库连接
-->
<dataSource type="POOLED"><!-- 基于连接池的数据源,使用连接池可避免重复与数据库进行连接 -->
<property name="driver" value="${jdbc.classname}"/><!-- 配置连接数据库的4个属性 -->
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<mappers><!-- 配置映射文件路径,加载映射文件 -->
<mapper resource="mapper/*.xml"/>
<!--
如果使用<package>来扫描包中的映射文件, 则必须使用sqlSession.getMapper(接口.class)来定义方法
并且xml映射文件的名字必须跟接口的名字一样
因为<package>扫描的不是xml的映射文件, 而是扫描的.java文件
-->
</mappers>
</configuration>
连接数据库属性文件:
jdbc.username=test0315
jdbc.password=123456
jdbc.url=jdbc:oracle:thin:@localhost:1521:xe
jdbc.classname=oracle.jdbc.OracleDriver
构建sqlSession:
package util;
import java.io.IOException;
import java.io.InputStream;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
/*
* 1, 构建SqlSessionFactory
InputStream in = Resources.getResourceAsStream("总配置文件所在的src下的路径");
SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(in);
2, 构建SqlSession(注意SqlSession不能以一个class成员变量的身份被返回)
SqlSession ss = ssf.openSession();
3, 直接运行
a: 直接运行映射文件中的sql语句
ss.select...
ss.insert...
ss.update...
ss.delete...
b: 使用接口映射配置文件
ss.getMapper(接口类名.class);
调用接口的方法
* */
public class MyBatisUtil {
public static SqlSession getSqlSession() {
return getSqlSessionFactory().openSession();//构建SqlSession实例
}
public static void main(String[] args) {
System.out.println(getSqlSession());
}
public static SqlSessionFactory getSqlSessionFactory() {
String resource = "conf/mybatis-config.xml";//总配置文件路径名
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream(resource);//使用输入流读取配置文件
} catch (IOException e) {
e.printStackTrace();
}
//构建SqlSessionFactory的实例
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return sqlSessionFactory;
}
实体model层:
package com.hanqi.model;
import java.util.Date;
import java.util.List;
//@Alias(value="ccc")//也可以通过在实体类上用注解起别名,优先于其他配置
public class Appuser {
private Integer ids;
private String uname;
private String pword;
private String realname;
private Date createtime;
private Integer mark;
private Integer sex;
private List<Accounts> accounts;
public Integer getIds() {
return ids;
}
public void setIds(Integer ids) {
this.ids = ids;
}
//以下省略
package com.hanqi.model;
import java.util.Date;
import java.util.List;
//@Alias(value="ccc")//也可以通过在实体类上用注解起别名,优先于其他配置
public class Appuser {
private Integer ids;
private String uname;
private String pword;
private String realname;
private Date createtime;
private Integer mark;
private Integer sex;
private List<Accounts> accounts;
public Integer getIds() {
return ids;
}
public void setIds(Integer ids) {
this.ids = ids;
}
//以下省略
接口层:
package com.hanqi.dao;
import java.util.List;
import com.hanqi.model.Accounts;
public interface AccountsDao {
int insertAccounts(Accounts accounts);
List<Accounts> selectAccounts();
int deleteAccountsByUserId(Integer ids);
}
package com.hanqi.dao;
import java.util.List;
import java.util.Map;
import com.hanqi.model.Appuser;
public interface AppuserDao {
int insertAppuser(Appuser appuser);
int updateAppuserByUname(Appuser appuser);
List<Appuser> selectAppuserBySex(Integer sex);
int deleteAppuserByIds(Integer ids);
List<Appuser> selectAppuserByParams(Map map);
}
数据访问映射文件xml:
<?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 namespace="com.hanqi.dao.AppuserDao"><!-- 配置每个实体类的映射文件/接口全路径名 -->
<sql id="Appuser"><!-- 配置sql语句 -->
ids,uname,pword,realname,createtime,mark,sex
</sql>
<!-- 动态添加数据操作,传递参数时,#{}中写的是Map集合中的键或者是实体类中的成员变量名-->
<!-- 需要进行数据类型声明,否则如果为null时无法放入数据库中的,报错 -->
<insert id="insertAppuser" parameterType="Appuser" ><!-- 被调用的id/方法名,结果类型为实体类 -->
<!--selectKey用于获取添加后数据的主键,oracle不支持自增,用BEFORE,需要通过getIds方法获取主键-->
<selectKey keyProperty="ids" order="BEFORE" resultType="Integer" keyColumn="ids">
select sq_test.nextval ids from dual<!--dual是指伪列 -->
</selectKey>
insert into appuser(
<include refid="Appuser"></include>
) values(
#{ids},
#{uname,jdbcType = VARCHAR},
#{pword,jdbcType = VARCHAR},
#{realname,jdbcType = VARCHAR},
sysdate,
1,
#{sex,jdbcType = DECIMAL})
</insert>
<!-- 通过条件进行修改操作 -->
<update id="updateAppuserByUname" parameterType="Appuser"><!-- 参数类型为实体类 -->
update appuser
<set>pword = #{pword},</set>
<where>and uname = #{uname}</where>
</update>
<!-- 通过性别条件进行一对多联合查询 -->
<resultMap type="Appuser" id="appuserList">
<id property="ids" column="IDS" />
<collection property="accounts" column="IDS"
select="com.hanqi.dao.AccountsDao.selectAccountsByUserId"></collection>
</resultMap>
<select id="selectAppuserBySex" resultMap="appuserList"
parameterType="Integer">
select * from appuser where sex = #{sex}
</select>
<!--通过id查询结果-->
<!--mybatis会自动将查询结果的列名与实体类的成员变量名相匹配,如果一样则调用set方法放进去 -->
<select id="selectAppuserByIds" parameterType="Integer" resultType="Appuser">
select * from appuser where ids = #{ids}
</select>
<!--通过id条件进行逻辑删除-->
<update id="deleteAppuserByIds" parameterType="Integer">
update appuser set mark = 0 where ids = #{ids}
</update>
<!--通过参数进行查询-->
<select id="selectAppuserByParams" parameterType="Map" resultType="Appuser">
select <include refid="Appuser"></include> from appuser
<where>
<if test="sex != null">
and sex=#{sex}
</if>
<if test="plist != null">
and ids in
<foreach collection="plist" item="ids" open="(" close=")" separator=",">
#{ids}
</foreach>
</if>
</where>
</select>
</mapper>
<?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 namespace="com.hanqi.dao.AccountsDao">
<insert id="insertAccounts" parameterType="Accounts" >
insert into accounts
<trim prefix="(" suffix=")" suffixOverrides=","><!--trim用法 -->
ids,
<if test="acco != null">
acco,
</if>
<if test="passwords != null">
passwords,
</if>
<if test="tel != null">
tel,
</if>
<if test="email != null">
email,
</if>
<if test="ptail != null">
ptail,
</if>
<if test="userid != null">
userid,
</if>
regtime,
<if test="note != null">
note,
</if>
mark,
</trim>
<trim prefix="values(" suffix=")" suffixOverrides=",">
sq_test.nextval,
<if test="acco != null">
#{acco},
</if>
<if test="passwords != null">
#{passwords},
</if>
<if test="tel != null">
#{tel},
</if>
<if test="email != null">
#{email},
</if>
<if test="ptail != null">
#{ptail},
</if>
<if test="userid != null">
#{userid},
</if>
sysdate,
<if test="note != null">
#{note},
</if>
1,
</trim>
</insert>
<!-- 通过id查询操作 -->
<select id="selectAccountsByUserId" parameterType="Integer" resultType="Accounts">
select * from accounts where userid = #{userid}
</select>
<!-- 一对一查询第一种方法 -->
<!-- <resultMap type="Accounts" id="accountslist">结果集定义为实体类
<id column="IDS" property="ids"/>id用来记录主键,column是取该列名下的值并赋值给实体类的成员变量中;
<result column="ACCO" property="acco"/>
<result column="PASSWORDS" property="passwords"/>
<result column="TEL" property="tel"/>
<result column="EMAIL" property="email"/>
<result column="PTAIL" property="ptail"/>
<result column="REGTIME" property="regtime"/>
<result column="NOTE" property="note"/>
<result column="UNAME" property="appuser.uname"/>
<result column="PWORD" property="appuser.pword"/>
</resultMap>
<select id="selectAccounts" resultMap="accountslist">id指接口中的方法,resultMap指定义好的集合
select * from accounts ac,appuser ap where ac.userid = ap.ids
</select> -->
<!-- 第二种方法,对象级联 -->
<resultMap type="Accounts" id="accountslist">
<association property="appuser" column="USERID" select="com.hanqi.dao.AppuserDao.selectAppuserByIds"></association>
</resultMap>
<!--一对一联合查询-->
<select id="selectAccounts" resultMap="accountslist">
select * from accounts
</select>
<!--通过条件逻辑删除-->
<update id="deleteAccountsByUserId" parameterType="Integer">
update accounts set mark = 0 where userid = #{userid}
</update>
</mapper>
测试类:
package test;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.session.SqlSession;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import com.hanqi.dao.AccountsDao;
import com.hanqi.dao.AppuserDao;
import com.hanqi.model.Accounts;
import com.hanqi.model.Appuser;
import util.MyBatisUtil;
public class JunitTest {
private SqlSession sqlSession;
private AppuserDao appuserDao;
private AccountsDao accountsDao;
@BeforeEach
void setUp() throws Exception {
sqlSession = MyBatisUtil.getSqlSession();
appuserDao = sqlSession.getMapper(AppuserDao.class);
accountsDao = sqlSession.getMapper(AccountsDao.class);
}
@AfterEach
void tearDown() throws Exception {
sqlSession.commit();
sqlSession.close();
}
@Test
void test() {
//添加操作
Appuser appuser = new Appuser();
appuser.setUname("小明1");
appuser.setPword("112345");
//appuser.setRealname("王五");
appuser.setSex(2);
int r = appuserDao.insertAppuser(appuser);
System.out.println(appuser.getIds());
System.out.println(r);
//多参数查询操作
Map map1 = new HashMap<>();
List<Integer> list1 = new ArrayList<>();
list1.add(361);
list1.add(442);
list1.add(495);
map1.put("sex", 2);
map1.put("plist", list1);
List<Appuser> list = appuserDao.selectAppuserByParams(map1);
p(list);
}
private void p(List<Appuser> list) {
for(Appuser a:list) {
System.out.println(a);
}
}
void test1() {
//修改操作
Appuser appuser = new Appuser();
appuser.setPword("hq1111");
appuser.setUname("admin");
int a = appuserDao.updateAppuserByUname(appuser);
System.out.println(a);
//逻辑删除操作
appuserDao.deleteAppuserByIds(361);
accountsDao.deleteAccountsByUserId(61);
}
}
分页及添加图片:
package test;
//省略引入
import util.MyBatisUtil;
public class JunitTest {
private SqlSession sqlSession;
private AppuserDao appuserDao;
private AccountsDao accountsDao;
@BeforeEach
void setUp() throws Exception {
sqlSession = MyBatisUtil.getSqlSession();
appuserDao = sqlSession.getMapper(AppuserDao.class);
accountsDao = sqlSession.getMapper(AccountsDao.class);
}
@AfterEach
void tearDown() throws Exception {
sqlSession.commit();
sqlSession.close();
}
@Test
void test() {
//添加图片
Appuser user = new Appuser();
user.setPword("1234567898090");
user.setRealname("新增用户");
user.setSex(2);
byte[] toux = null;
InputStream in = null;
try {
in = new FileInputStream("C:\\Users\\Administrator\\Desktop\\一阶段项目\\图片/购物人物.jpg");
try {
toux = new byte[in.available()];
in.read(toux);
} catch (IOException e) {
e.printStackTrace();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}finally{
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
user.setToux(toux);
int q = appuserDao.insertAppuser(user);
System.out.println(q);
//输出图片
Appuser user2 =new Appuser();
user.setIds(620);
Appuser user1 = appuserDao.selectAppuserByIds(user2);
byte[] toux1 = user1.getToux();
OutputStream out =null;
try {
out = new FileOutputStream("C:\\Users\\Administrator\\Desktop\\一阶段项目\\图片/购物人物44.jpg");
try {
out.write(toux1);
} catch (IOException e) {
e.printStackTrace();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}finally {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//sqlSession.clearCache();清空1级缓存
//<select useCache="true">清空2级缓存
/*逻辑分页,与物理分页不同的是已经全部查询出来再进行分页
RowBounds row = new RowBounds(5,2);意为跳过5行取2行(page - 1) * rows, rows
直接将参数放入即可,如:
List<Appuser> list = appuserDao.selectAppuserByParams(map1,row);*/
}
}
特殊符号表达式:
< < 小于号
> > 大于号
& & 和
' ’ 单引号
" " 双引号
例: select * from emp where sal < #{sal}
<![CDATA[]]>交由下一级去处理,即数据库。
例:<![CDATA[select * from emp where sal < #{sal}]]>