资源文件可以下载,在我个人的资源文件里(MyBatis1.rar)
mybatis简介
mybatis框架执行原理
mybatis初次的入门案例
mybatis配置文件详解
mybatis 方法多参数的处理
mybatis返回主键值
sql代码段
自定义结果类型ResultMap
(一)Mybatis简介
mybatis本身是一个轻量级的持久化层框架(1.何为持久化。2.何为序列化操作),本身也是基于JDBC的封装(JDBC的链接步骤).开发者本身更多的关注SQL语句的执行效率,除此之外mybatis也是一个半自动的ORM映射框架(支持一对一,一对多的实现,多对多采用两个一对多进行实现)
注意:实际的开发过程中,因为大量的关系相互映射的存在,在查询数据这一块不便于后期项目本身的项目维护扩展。所以更多的方向是思考数据库中的设计和利用本身mybatis提供的数据自定义封装和其它的类似缓存机制的特点,解决开发中的数据设计结构
mybatis优势:
1、比起jdbc减少了一些重复的代码量操作,也方便能够集成到后期的管理框架中
2、mybatis提供在xml中编写sql语句,不直接将sql语句入侵到代码中
3、分别提供的xml标签和mapper标签(xml标签可实现动态SQL语句,也就是嵌入条件判断和循环,比较类似存储函数),mapper标签支持对象正确的解析至数据库中
mybatis框架执行原理
sqlConfigXMl配置文件(一个全局的配置文件)(可配置映射文件和连接数据源和事务等)
通过配置文件构建出可构建操作数据会话的会话工厂,也就是我们常说的sqlSessionFactory(涉及工厂模式代码设计)
通过sqlSessionFactory生产出相互独立的sqlsession,为什么是独立的会话,既然是独立的会话,那也有全局的会话(简单提及缓存)进行数据库层面上面的操作
sqlsession之所以能够操作,依赖一个叫Executor的执行器,通过该执行器进行数据库的CRUD操作
Executor的执行器需要操作CRUD的动作由谁而来,就是由mapperstatement对象读取mapper映射文件
mybatis入门搭建步骤
1.在src目录下配置一个log4j.properties(可选)方便我们监听到mybatis进行的动作(sql执行过程)
\# set level
log4j.rootLogger=DEBUG, stdout
\# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
2、构建一个简单的web项目,在lib目录下导入如下的jar包
3、src目录下构建mybatis的全局配置文件mybatisCfg.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>
<!-- 数据库连接环境配置 -->
<environments default="development">
<!-- 标明mybaitis环境 id唯一 -->
<environment id="development">
<!-- JDBC – 这个配置直接简单使用了 JDBC 的提交和回滚设置。 它依赖于从数据源得 到的连接来管理事务范围。JDBC默认是自动提交 -->
<transactionManager type="JDBC" />
<!-- 采用数据库连接池 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<!-- 避免环境的不统一,造成数据操作乱码 -->
<property name="url"
<!-- 分别是://数据库地址:数据库端口号/数据库名称?字符集设置 -->
value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8" />
<!-- 数据库连接用户名及密码 -->
<property name="username" value="root" />
<property name="password" value="123456" />
</dataSource>
</environment>
</environments>
<!-- 添加需要被映射的文件(即编写Mapper映射文件) -->
<mappers>
<mapper resource="com/wwj/dao/PersonMapper.xml" />
</mappers>
</configuration>
构建模型类 com.wwj.model 和 数据库表
import java.io.Serializable;
import java.util.Date;
/**
* 基本的模型类
* @author wwj
*对象序列化是一个用于将对象状态转换为字节流的过程,可以将其保存到磁盘文件中或通过网络发送到任何其他程序;从字节流创建对象的相反的过程称为反序列化。而创建的字节流是与平台无关的,在一个平台上序列化的对象可以在不同的平台上反序列化。
*无需序列化的变量使用transient
*/
public class Person implements Serializable {
//Java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的
//这里是用来表明版本的一致性
private static final long serialVersionUID = 2680875170108959939L;
private Integer id;
private String name;
private Date bir;
private String address;
//自行get和set
}
构建数据库表
创建数据层的操作也就是mapper的操作接口
/**
* person层的操作
*
* @author Yun
*
*/
public interface PersonDao {
/**
* 新增用户
*
* @param p
* 传入需要新增的对象
* @return 0,1代表结果
*/
int savePerson(Person p);
/**
* 更新用户对象
* @param p 需要被更新的对象
* @return 0,1代表结果
*/
int updatePerson(Person p);
/**
* 根据用户id进行删除
* @param id 唯一用户id
* @return 0,1代表结果
*/
int deletePersonById(int id);
/**
* 获取所有的信息
* @return 所有的人员信息
*/
List<Person> getPersonInfos();
}
构建对应的mapper映射文件命名为PersonMapper.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接口的全路径名称 -->
<mapper namespace="com.wwj.dao.PersonDao">
<insert id="savePerson" parameterType="com.wwj.model.Person">
insert into
person(name,address,bir) values(#{name},#{address},#{bir});
</insert>
<update id="updatePerson" parameterType="com.wwj.model.Person">
update person set
name=#{p.name},address=#{address},bir=#{bir}
where id=#{id}
</update>
<delete id="deletePersonById" parameterType="int">
delete from person
where id=#{id}
</delete>
<select id="getPersonInfos" resultType="com.wwj.model.Person">
select * from person
</select>
</mapper>
编写完Mapper后一点要将映射加入到全局配置mybatisCfg.xml,文件中,否者不会生效
编码测试
/**
* 测试mybatis的CRUD操作
*
* @author wwj
*
*/
public class TestMybatis {
public static void main(String[] args) throws IOException, ParseException {
/*
* 日期上面的处理
*/
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd");
String format = sf.format(new Date());
Date parse = sf.parse(format);
InputStream is = Resources.getResourceAsStream("mybatisCfg.xml");
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(is);
// 生成 session
SqlSession session = build.openSession();
Person per = new Person();
per.setName(" 小王 ");
per.setAddress(" 重庆 ");
per.setBir(parse);
// 操作数据
int insert = session.insert("savePerson",per);
// 提交事务
session.commit();
// 关闭 session
session.close();
}
}
(三)Mybatis配置文件详解
全局配置文件mybatisCfg.xml详解:
environments环境配置,可以配置多种环境 default指定使用某种环境.
transactionManager事务管理器有两种取值JDBC,managed.我们选择jdbc即可
dataSource配置数据源,采用默认的连接池选择项POOLED
mappers里面填入需要进行数据操作xml标签用于执行的动作
映射的数据操作文件需要和接口保持同个路径(可以把mapper当成接口的实现类)
映射文件PersonMapper.xml详解
namespace表明需要对应动作的空间即是接口所在的全路径名称
id与接口中的方法保持一致
parameterType填写自定义对象的全路径名称
接收参数采用 #{objAttrName}
(四)Mybatis方法多参数接收(代码示例)
1、索引接收(了解即可)
//在接口中
List<Person> getPersonInfosByNameAndID(String name ,int id);
//在影射文件中
<select id="getPersonInfosByNameAndID" >
select * from person where name = #{0} and id = #{1}
</select>
2、map接收(重点)
//在接口中
/**
* 根据map进行查询
* @param attrs
* key1 id key2 name
* @return
*/
List<Person> getPersonInfosByMap(Map attrs);
//在影射文件中
<select id="getPersonInfosByMap" parameterType="java.util.Map" resultType="com.wwj.model.Person">
select * from person where id =
#{id} and name = #{name}
</select>
//----------------调用测试类中
Map<String,Object> attrs = new HashMap<>();
attrs.put("id", 2);
attrs.put("name", "小王");
session.selectList("getPersonInfosByMap", attrs);
3、.注解@Param接收(重点)
/**
* 根据用户唯一id查询信息
* @param id
* @return
*/
Person getPersonInfo(@Param("pid") int id);
//---------------动作实现
<select id="getPersonInfo"
resultType="com.wwj.model.Person">
select * from person where id =#{pid}
</select>
//---------调用测试
session.selectOne("getPersonInfo", 2);
(五)Mybatis立即返回主键
应用场景:当我们需要在当前事务插入数据后立即获取数据的主键id,做下一步额外操作,并且不因为并发高的情况下取错值而考虑
<insert id="savePerson" parameterType="com.wwj.model.Person">
<selectKey keyProperty="id" resultType="int" order="AFTER">
select last_insert_id()
</selectKey>
insert into
person(name,address,bir) values(#{name},#{address},#{bir});
</insert>
//------------------------------------------------------------
// 操作数据
Person p = new Person();
p.setName("小小王");
p.setBir(parse);
int result = session.insert("savePerson", p);
System.out.println(result);
//原本我们的p对象是没有主键id的,主键id是数据库表中id项自增长自动生成的,
//当我们设置了立即返回主键后,在我们插入一个对象时,我们就可以立即获取并返回数据库生成的那个id。所以下面可以打印出id
System.out.println(p.getId());
keyProperty=”返回主键的id的属性名”
resultType=”主键类型”
order=”“什么时候执行,在SQL执行前还是执行后执行,两个取值:BEFORE和AFTER
select last_insert_id()取到最后生成的主键,只在当前事务中取
sql代码段
如果场景中有大量的重复的公共sql语句,那么可以考虑使用声明公共的部分
示例如下:
/**
* sql片段
* @param id 根据用户的id查询姓名
* @return
*/
String getPersonName(@Param("pid") int id);
//--------动作实现
<sql id="nameCol"> name</sql>
<select id="getPersonName" resultType="java.lang.String">
select
<!--在这里引入公共的sql代码段-->
<include refid="nameCol"></include>
from person where
id =
#{pid}
</select>
//--------测试调用
String name = session.selectOne("getPersonName",2);
System.out.println(name);
自定义结果类型ResultMap(开发中长期使用)
应用场景:假设我们的实际开发过程中,数据表组合字段多,又不想关心配置映射关系,只想关心sql语句,以及结果,并且也关心sql语句的效率
假设2张表 person和card 1:m关系
连接查询需要person中的人名和card表中的卡号名字
//----- 实体类
public class Card {
private String cname;
}
public class Person implements Serializable {
private Integer id;
private String name;
private Date bir;
private String address;
private List<Card> cards;
}
//----构建自定义的resultmap封装 注意 collection(集合)association(联系)
<resultMap type="com.wwj.model.Person" id="personRS">
<!--column指向数据库列名 property指向pojo对象中字段名 -->
<result column="name" property="name" />
<!-- property指的是在bean中字段名 ofType类的全定向名 -->
<collection property="cards" ofType="com.wwj.model.Card">
<result column="cname" property="cname" />
</collection>
</resultMap>
//--------映射的动作实现
<select id="getPersonsOfCard" resultMap="personRS">
select
person.name,card.cname
from person
INNER JOIN card
on person.id = card.pid
</select>
//--------代码操作
List<Person> persons = session.selectList("getPersonsOfCard");
for (Person person : persons) {
System.out.println(person.getCards().get(0).getCname());
System.out.println(person.getCards().get(1).getCname());
}