1 注解形式
1.0 注解形式和配置形式可以共存
属于的jar完全相同
- 装备sql
DROP TABLE student;
CREATE TABLE student(
sid INT PRIMARY KEY AUTO_INCREMENT,
sname VARCHAR(100),
sex CHAR(1),
sage INT,
sdy BOOLEAN,
score FLOAT(4,1)
);
INSERT INTO student VALUES(
NULL,
SUBSTRING(REPLACE(UUID(),'-',''),1,10),
IF(RAND()>0.5,'男','女'),
TRUNCATE(RAND()*10+15,0),
RAND()>0.5,
TRUNCATE(RAND()*100,1)
);
1.2 mapper接口
package com.zhiyou100.test04;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
public interface StudentMapper {
public List<Student> getAll1();
@Select("select * from student")
public List<Student> getAll2();
@Insert("insert into student(sname,sage,sex,sdy,score) values(#{sname},#{sage},#{sex},#{sdy},#{score})")
public int addOne(Student s);
@Delete("delete from student where sid=#{sid}")
public int deleteOne(int sid);
@Update("update student set sname=#{sname},sex=#{sex},sdy=#{sdy},score=#{score},sage=#{sage} where sid=#{sid}")
public int updateOne(Student s);
@Select("select * from student where sdy=#{0} and score <= #{1} and sex=#{2}")
public List<Student> findSome1(boolean dy,float minScore,char sex);
@Select("select * from student where sdy=#{sdy} and score <= #{score} and sex=#{sex}")
public List<Student> findSome2(@Param("sdy")boolean dy,@Param("score")float minScore,@Param("sex")char sex);
}
1.3 sql配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zhiyou100.test04.StudentMapper">
<select id="getAll1" resultType="Student">
select * from student
</select>
</mapper>
1.4 实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain=true)
public class Student implements Serializable{
private Integer sid;
private String sname;
private String sex;
private Integer sage;
private Boolean sdy;
private Float score;
}
2 动态sql
2.0 概念
在sql映射文件中 通过标签实现sql语句的流程控制(跳转 循环 选择)
2.1 if标签
<!-- //如果score和sex有效 就作为查询条件 如果无效就查询所有
public List<Student> getAll2(Student s);-->
<select id="getAll2" parameterType="Student" resultType="Student">
select * from student where 1=1
<!-- test属性中的score 必须是参数对象的属性名 -->
<if test="score gte 0 and score lte 100">
and score >= #{score}
</if>
<!-- test属性中的sex 必须是参数对象的属性名 -->
<if test='sex eq "男" or sex eq "女"'>
and sex = #{sex}
</if>
</select>
<!-- //如果sname不为null 就把sname作为条件 否则就查询所有
public List<Student> getAll3(Student s);-->
<select id="getAll3" parameterType="Student" resultType="Student">
select * from student where 1=1
<if test="sname != null">
and sname = #{sname}
</if>
</select>
- 注意事项1:if标签的test属性中可以使用的运算符
<!--
"," ...
"=" ...
"?" ...
"||" ...
"or" ...
"&&" ...
"and" ...
"|" ...
"bor" ...
"^" ...
"xor" ...
"&" ...
"band" ...
"==" ...
"eq" ...
"!=" ...
"neq" ...
"<" ...
"lt" ...
">" ...
"gt" ...
"<=" ...
"lte" ...
">=" ...
"gte" ...
"in" ...
"not" ...
"<<" ...
"shl" ...
">>" ...
"shr" ...
">>>" ...
"ushr" ...
"+" ...
"-" ...
"*" ...
"/" ...
"%" ...
-->
- 注意2:在test属性中使用字符串方法:
<!--方式1-->
<if test="sex eq '男'.toString() or sex eq '女'.toString() ">
sex=#{sex}
</if>
<!--方式2-->
<if test='sex eq "男" or sex eq "女" '>
sex=#{sex}
</if>
2.2 where标签
智能判断是否添加where
<!-- //如果score和sex有效 就作为查询条件 如果无效就查询所有
public List<Student> testWhere1(Student s);-->
<select id="testWhere1" parameterType="Student" resultType="Student">
select * from student
<where>
<if test="score gte 0 and score lte 100">
and score >= #{score}
</if>
<if test="sex eq '男'.toString() or sex eq '女'.toString()">
and sex = #{sex}
</if>
</where>
</select>
2.3 set标签
只适用于update语句:智能判断是否保留sql片段后面的,
<!-- //如果属性有值就修改 没有值就保持原来的值
public int testSet01(Student s);-->
<update id="testSet01" parameterType="Student">
update student
<set> <!-- 智能判断去除sql片段后面的, -->
<if test="sname != null">
sname=#{sname},
</if>
<if test="sex != null">
sex=#{sex},
</if>
<if test="score != null">
score=#{score},
</if>
<if test="sage != null">
sage=#{sage},
</if>
<if test="sdy != null">
sdy=#{sdy},
</if>
</set>
where sid=#{sid}
</update>
2.4 choose标签
实现条件的多分支判断
<!-- 如果性别有值 把性别作为唯一条件 如果性别没值 再判断党员 如果党员没值 再判断年龄 如果都没值 条件是score>60-->
<!-- 使用if实现 -->
<select id="testChoose01" parameterType="Student" resultType="Student">
select * from student
<where>
<if test="sex != null">
sex = #{sex}
</if>
<if test="sex == null and sdy != null ">
sdy = #{sdy}
</if>
<if test="sex == null and sdy == null and sage != null">
sage = #{sage}
</if>
<if test="sex == null and sdy == null and sage == null">
score > 60
</if>
</where>
</select>
<!-- 使用choose实现 -->
<select id="testChoose02" parameterType="Student" resultType="Student">
select * from student
<where>
<choose>
<when test="sex != null ">
sex = #{sex}
</when>
<when test="sdy != null ">
sdy = #{sdy}
</when>
<when test="sage != null ">
sage = #{sage}
</when>
<otherwise>
score > 60
</otherwise>
</choose>
</where>
</select>
2.5 trim标签
<!-- //如果属性有值就修改 没有值就保持原来的值public int testTrim01(Student s);-->
<!-- trim标签的是prefix属性:整体前面加前缀 -->
<!-- trim标签的是suffix属性:整体后面加后缀 -->
<!-- trim标签的是suffixOverrides属性:智能判断每个sql片段后面的指定字符是否保留-->
<!-- trim标签的是prefixOverrides属性:智能判断每个sql片段前面的指定字符是否保留-->
<!-- trim标签的是prefix属性:整体前面加前缀 -->
<update id="testTrim01" parameterType="Student">
update student
<trim prefix="set" suffix="where" suffixOverrides=",">
<if test="sname != null">
sname=#{sname},
</if>
<if test="sex != null">
sex=#{sex},
</if>
<if test="score != null">
score=#{score},
</if>
<if test="sage != null">
sage=#{sage},
</if>
<if test="sdy != null">
sdy=#{sdy},
</if>
</trim>
sid=#{sid}
</update>
2.6 foreach标签
<!-- //根据参数数组的所有元素的id值进行查询public List<Student> testForeach01(int[] arr);-->
<!-- select * from student where sid in(1,5,7,8,9,0) -->
<!-- foreach标签的collection属性:指定参数对象的类型::array/list -->
<!-- foreach标签的open属性:拼凑成的整个sql片段整体前面加的内容-->
<!-- foreach标签的close属性:拼凑成的整个sql片段整体后面加的内容 -->
<!-- foreach标签的item属性:为变量集合时 定义的变量来接受集合中的元素 -->
<!-- foreach标签的separator属性:每个sql片段后面智能添加的字符 -->
<!-- foreach标签的index属性:为变量集合时 定义的变量来接受元素的下标-->
<select id="testForeach01" parameterType="int[]" resultType="Student">
select * from student where sid in
<foreach collection="array" open="(" close=")" item="id" separator=",">
#{id}
</foreach>
</select>
<!-- 一次插入多行public int testForeach02(List<Student> list);-->
<!-- insert into student(sid,sname,sex,sdy,score,sage) values(xxx,xx,xx),(xxx,xx,xx),(xxx,xx,xx) -->
<insert id="testForeach02" parameterType="java.util.List">
insert into student(sid,sname,sex,sdy,score,sage)
<foreach collection="list" open="values" close="" index="i" item="s" separator=",">
(#{i}+1000,#{s.sname},#{s.sex},#{s.sdy},#{s.score},#{s.sage})
</foreach>
</insert>
2.7 bind标签
<!--bind标签:定义变量 此变量可以在sql片段中使用-->
<!-- List<Student> getAll10(String s); -->
<select id="getAll10" resultType="Student">
select * from student where sname like concat('%',#{sname},'%')
</select>
<!-- List<Student> getAll11(String s); -->
<select id="getAll11" resultType="Student" parameterType="String">
<bind name="sn" value="'%' + _parameter + '%'"/>
select * from student where sname like #{sn}
</select>
3 动态sql的条件中怎么获取参数的值
3.1 参数是对象:直接通过属性名获取参数对象对应的属性值
<!-- 参数是对象:test中变量名和参数对象的属性名一致即可 -->
<!-- 如果score和sex,sage有效 就作为查询条件 如果无效就查询所有
public List<Student> getAll20(Student)-->
<select id="getAll20" resultType="Student" parameterType="Student">
select * from student
<where>
<!-- 标签test条件中的变量score/sex/sage 必须和参数对象Student的属性名一致即可 -->
<if test="score != null">
score >= #{0} and <!-- 在sql片段中可以使用索引来 获取占位符的值 -->
</if>
<if test="sex != null">
sex >= #{1} and
</if>
<if test="sage != null">
sage >= #{2}
</if>
</where>
</select>
3.2 参数是多个单值参数时:通过paramx来指向第x个参数的值
<!-- 多值参数:使用paramX来指向第x的参数的值 -->
<!-- 如果score和sex,sage有效 就作为查询条件 如果无效就查询所有
public List<Student> getAll21(Float score,String sex,Integer sage)-->
<select id="getAll21" resultType="Student">
select * from student
<where>
<!-- 错误写法:会在参数对象中找score属性:-->
<if test="param1 != null">
score >= #{0} and <!-- 在sql片段中可以使用索引来 获取占位符的值 -->
</if>
<if test="param2 != null">
sex >= #{1} and
</if>
<if test="param3 != null">
sage >= #{2}
</if>
</where>
</select>
3.3 参数是一个 并且是单值参数时:通过_parameter来指向此唯一的单值参数
<!-- 如果说只有一个参数:但不是对象 而是单值参数时:可以通过parameter来指向此唯一参数的值 -->
<!-- 如果score和sex,sage有效 就作为查询条件 如果无效就查询所有
public List<Student> getAll22(Float score)-->
<select id="getAll22" resultType="Student" parameterType="float">
select * from student
<where>
<if test="_parameter != null">
score >= #{0} <!-- 在sql片段中可以使用索引来 获取占位符的值 -->
</if>
</where>
</select>
4 反向工程
4.0 概念
持久层框架的一个功能:实现由表自动生成实体类和mapper接口等
由实体类自动生成表
4.1 导包
log4j
jdbc
myabtis
mybatis-generator-core-1.3.2.jar
4.2 创建表
SHOW CREATE TABLE student;
CREATE TABLE `student` (
`sid` INT(11) NOT NULL AUTO_INCREMENT,
`sname` VARCHAR(100) DEFAULT NULL,
`sex` CHAR(1) DEFAULT NULL,
`sage` INT(11) DEFAULT NULL,
`sdy` TINYINT(1) DEFAULT NULL,
`score` FLOAT(4,1) DEFAULT NULL,
PRIMARY KEY (`sid`)
) ENGINE=INNODB AUTO_INCREMENT=1004 DEFAULT CHARSET=utf8;
ALTER TABLE teacher ADD tsalary FLOAT(9,2);
ALTER TABLE teacher ADD tsex CHAR(1);
UPDATE teacher SET tsalary=TRUNCATE(RAND()*100000,0),tsex=IF(RAND()>0.5,"男","女");
SHOW CREATE TABLE teacher;
CREATE TABLE `teacher` (
`tid` INT(11) NOT NULL AUTO_INCREMENT,
`tname` VARCHAR(100) DEFAULT NULL,
`tage` INT(11) DEFAULT NULL,
`tsalary` FLOAT(9,2) DEFAULT NULL,
`tsex` CHAR(1) DEFAULT NULL,
PRIMARY KEY (`tid`)
) ENGINE=INNODB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8;
4.3 创建反向工程的配置文件:
名字随意
位置随意
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd" >
<generatorConfiguration>
<!-- 一个数据库一个context -->
<context id="infoGuardian">
<!-- 注释 -->
<commentGenerator >
<!-- suppress禁止 -->
<property name="suppressAllComments" value="true"/><!-- 生成代码的时候是否生成注释,true是取消注释,false会生成注释 -->
<property name="suppressDate" value="true" /> <!-- 是否生成注释代时间戳-->
</commentGenerator>
<!-- jdbc连接 -->
<jdbcConnection
driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/db_1"
userId="root"
password="root" />
<!-- 类型转换 -->
<javaTypeResolver>
<!-- 默认为false,可以把数据库中的decimal以及numeric类型解析为Integer,为true时会解析为java.math.BigDecimal) -->
<property name="forceBigDecimals" value="false"/>
</javaTypeResolver>
<!-- 生成实体类地址 -->
<javaModelGenerator targetPackage="com.zhiyou100.entity"
targetProject=".\src" >
<!-- 是否在当前路径下新加一层schema,
如果为fase路径com.shop.pojo, 为true:com.shop.pojo.[schemaName] 这个情况主要是oracle中有,mysql中没有schema -->
<property name="enableSubPackages" value="false"/>
<!-- 是否针对string类型的字段在set的时候进行trim调用 -->
<property name="trimStrings" value="true"/>
</javaModelGenerator>
<!-- 生成mapxml文件 -->
<sqlMapGenerator targetPackage="com.zhiyou100.mapper"
targetProject=".\src" >
<!-- 是否在当前路径下新加一层schema,
如果为fase路径com.shop.dao.mapper, 为true:com.shop.dao.mapper.[schemaName] 这个情况主要是oracle中有,mysql中没有schema -->
<property name="enableSubPackages" value="false" />
</sqlMapGenerator>
<!-- 生成mapxml对应client,也就是接口dao -->
<javaClientGenerator targetPackage="com.zhiyou100.dao"
targetProject=".\src" type="XMLMAPPER" >
<!-- 是否在当前路径下新加一层schema,
如果为fase路径com.shop.dao.mapper, 为true:com.shop.dao.mapper.[schemaName] 这个情况主要是oracle中有,mysql中没有schema -->
<property name="enableSubPackages" value="false" />
</javaClientGenerator>
<!-- 配置表信息 -->
<table schema="" tableName="student"
domainObjectName="Student" enableCountByExample="false"
enableDeleteByExample="false" enableSelectByExample="false"
enableUpdateByExample="false">
<!-- schema即为数据库名 tableName为对应的数据库表, domainObjectName是要生成的实体类的名字, enable*ByExample指的是否生成 对应的example类,Mybatis Generator默认设置会生成一大堆罗哩罗嗦的Example类,主要是用各种不同的条件来操作数据库,大部分是用不到的,用到的时候手工修改mapper和接口文件就行了 -->
<!-- 忽略列,不生成bean 字段 -->
<ignoreColumn column="smail" />
</table>
<table schema="" tableName="teacher"
domainObjectName="Teacher" enableCountByExample="false"
enableDeleteByExample="false" enableSelectByExample="false"
enableUpdateByExample="false">
<!-- schema即为数据库名 tableName为对应的数据库表, domainObjectName是要生成的实体类的名字, enable*ByExample指的是否生成 对应的example类,Mybatis Generator默认设置会生成一大堆罗哩罗嗦的Example类,主要是用各种不同的条件来操作数据库,大部分是用不到的,用到的时候手工修改mapper和接口文件就行了 -->
</table>
</context>
</generatorConfiguration>
4.4 为反向工程创建一个启动类
package test01;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.internal.DefaultShellCallback;
public class Test01 {
public static void main(String[] args) throws Exception{
List<String> warnings = new ArrayList<String>();
boolean overwrite = true;
File configFile = new File("src/generater_config.xml");
ConfigurationParser cp = new ConfigurationParser(warnings);
Configuration config = cp.parseConfiguration(configFile);
DefaultShellCallback callback = new DefaultShellCallback(overwrite);
MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
myBatisGenerator.generate(null);
}
}
4.5 运行就生成:实体类+mapper接口+mapper映射文件
4.6 注意
1:反向工程生成的内容不是一劳永逸的:可以根据需求更改
2:专门有个项目用于反向工程 把生成的文件添加到开发项目中
5 mybatis的缓存
参考:https://www.cnblogs.com/happyflyingpig/p/7739749.html
5.0 概念
缓存是所有持久层框架都具有的一种功能:把每次的请求得的sql语句和请求结果以键值对的形式存储起来
当再次请求相同内容时 不再去访问数据库 而是直接使用上一次请求的结果
缓存的作用:降低数据库服务器的压力+提高查询效率
一旦执行了dml语句:缓存就需要清空
mybatis的一级缓存:基于sqlsession的缓存:
5.1 一级缓存
Mybatis对缓存提供支持,但是在没有配置的默认情况下,它只开启一级缓存,一级缓存只是相对于同一个SqlSession而言。所以在参数和SQL完全一样的情况下,我们使用同一个SqlSession对象调用一个Mapper方法,往往只执行一次SQL,因为使用SelSession第一次查询后,MyBatis会将其放在缓存中,以后再查询的时候,如果没有声明需要刷新,并且缓存没有超时的情况下,SqlSession都会取出当前缓存的数据,而不会再次发送SQL到数据库。
- 一级缓存的生命周期
a、MyBatis在开启一个数据库会话时,会 创建一个新的SqlSession对象,SqlSession对象中会有一个新的Executor对象。Executor对象中持有一个新的
PerpetualCache对象;当会话结束时,SqlSession对象及其内部的Executor对象还有PerpetualCache对象也一并释放掉。
b、如果SqlSession调用了close()方法,会释放掉一级缓存PerpetualCache对象,一级缓存将不可用。
c、如果SqlSession调用了clearCache(),会清空PerpetualCache对象中的数据,但是该对象仍可使用。
d、SqlSession中执行了任何一个update操作(update()、delete()、insert()) ,都会清空PerpetualCache对象的数据,但是该对象可以继续使用
- 代码测试
//1 加载核心配置文件 创建SqlSessionFactory对象
InputStream inputStream = Resources.getResourceAsStream("mybatis_config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//2 通过SqlSessionFactory对象和SqlSession对象
SqlSession sqlSession=sqlSessionFactory.openSession();
StudentMapper sm=sqlSession.getMapper(StudentMapper.class);
System.out.println(sm.selectByPrimaryKey(1));
System.out.println("-----");
//使用了缓存
System.out.println(sm.selectByPrimaryKey(1));
//sqlSession.clearCache();
//注意2:sqlsession调用了clearCache方法:清空缓存:一级缓存清空
//注意3:如果当前sqlsession执行了dml语句:一级缓存清空
sm.deleteByPrimaryKey(3);
System.out.println("-----");
System.out.println(sm.selectByPrimaryKey(1));
//5 关闭session
sqlSession.close();
//重新获取sqlsession
//注意1:sqlsession调用close方法:关闭连接后:一级缓存清空
// sqlSession=sqlSessionFactory.openSession();
// sm=sqlSession.getMapper(StudentMapper.class);
// System.out.println("-----");
// System.out.println(sm.selectByPrimaryKey(1));
5.2 二级缓存
1 概念
二级缓存是基于sqlsesssionfactory的缓存:
二级缓存需要持久化保存到文件中
2 步骤
- 实体类实现序列化接口
public class Student implements Serializable{}
- 在核心配置文件中生命使用二级缓存
<settings>
...
<!--这个配置使全局的映射器(二级缓存)启用或禁用缓存-->
<setting name="cacheEnabled" value="true" />
</settings>
- 在使用二级缓存的mapper映射文件中 声明开启二级缓存
<!--开启本mapper的namespace下的二级缓存-->
<!--
eviction:代表的是缓存回收策略,目前MyBatis提供以下策略。
(1) LRU,最近最少使用的,一处最长时间不用的对象
(2) FIFO,先进先出,按对象进入缓存的顺序来移除他们
(3) SOFT,软引用,移除基于垃圾回收器状态和软引用规则的对象
(4) WEAK,弱引用,更积极的移除基于垃圾收集器状态和弱引用规则的对象。这里采用的是LRU,
移除最长时间不用的对形象
flushInterval:刷新间隔时间,单位为毫秒,这里配置的是100秒刷新,如果你不配置它,那么当
SQL被执行的时候才会去刷新缓存。
size:引用数目,一个正整数,代表缓存最多可以存储多少个对象,不宜设置过大。设置过大会导致内存溢出。
这里配置的是1024个对象
readOnly:只读,意味着缓存数据只能读取而不能修改,这样设置的好处是我们可以快速读取缓存,缺点是我们没有
办法修改缓存,他的默认值是false,不允许我们修改
-->
<cache eviction="LRU" flushInterval="100000" readOnly="true" size="1024"/>
- 在使用缓存的sql标签中指定使用二级缓存
<!--可以通过设置useCache来规定这个sql是否开启缓存,ture是开启,false是关闭-->
<select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Integer" useCache="true">
select
<include refid="Base_Column_List" />
from student
where sid = #{sid,jdbcType=INTEGER}
</select>
<select id="getAll02" resultType="Student" useCache="true">
select * from student
</select>
3 测试
package test01;
import java.io.File;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.internal.DefaultShellCallback;
import com.zhiyou100.mapper.StudentMapper;
public class Test03 {
public static void main(String[] args) throws Exception{
//1 加载核心配置文件 创建SqlSessionFactory对象
InputStream inputStream = Resources.getResourceAsStream("mybatis_config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//2 通过SqlSessionFactory对象和SqlSession对象
SqlSession sqlSession=sqlSessionFactory.openSession();
StudentMapper sm=sqlSession.getMapper(StudentMapper.class);
System.out.println(sm.selectByPrimaryKey(1));
System.out.println("-----");
System.out.println("获取所有::使用缓存:"+sm.getAll02());
System.out.println("-----");
System.out.println("获取所有::不使用缓存:"+sm.getAll03());
System.out.println("-----");
//5 关闭session
sqlSession.close();
//重新获取sqlsession
//注意1:sqlsession调用close方法:关闭连接后:一级缓存清空
sqlSession=sqlSessionFactory.openSession();
sm=sqlSession.getMapper(StudentMapper.class);
System.out.println(sm.selectByPrimaryKey(1));
System.out.println("-----");
System.out.println("获取所有::使用缓存:"+sm.getAll02());
System.out.println("-----");
System.out.println("获取所有::不使用缓存:"+sm.getAll03());
}
}
6 模拟mybatis的原始dao方式
6.1 数据库准备
CREATE TABLE `student` (
`sid` int(11) NOT NULL AUTO_INCREMENT,
`sname` varchar(100) DEFAULT NULL,
`sex` char(1) DEFAULT NULL,
`sage` int(11) DEFAULT NULL,
`sdy` tinyint(1) DEFAULT NULL,
`score` float(4,1) DEFAULT NULL,
PRIMARY KEY (`sid`)
) ENGINE=InnoDB AUTO_INCREMENT=1005 DEFAULT CHARSET=utf8
6.2 创建项目:模拟mybatis的所有内容
需要jdbc驱动jar
6.3 创建实体类
必须有无参数的构造方法
package com.zhiyou100.entity;
import java.io.Serializable;
public class Student implements Serializable{
private Integer sid;
private String sname;
private String sex;
private Integer sage;
private Boolean sdy;
private Float score;
public Integer getSid() {
return sid;
}
public void setSid(Integer sid) {
this.sid = sid;
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname == null ? null : sname.trim();
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex == null ? null : sex.trim();
}
public Integer getSage() {
return sage;
}
public void setSage(Integer sage) {
this.sage = sage;
}
public Boolean getSdy() {
return sdy;
}
public void setSdy(Boolean sdy) {
this.sdy = sdy;
}
public Float getScore() {
return score;
}
public void setScore(Float score) {
this.score = score;
}
@Override
public String toString() {
return "Student [sid=" + sid + ", sname=" + sname + ", sex=" + sex + ", sage=" + sage + ", sdy=" + sdy
+ ", score=" + score + "]";
}
public Student(Integer sid, String sname, String sex, Integer sage, Boolean sdy, Float score) {
super();
this.sid = sid;
this.sname = sname;
this.sex = sex;
this.sage = sage;
this.sdy = sdy;
this.score = score;
}
public Student() {
super();
}
}
6.4 准备
- 工具类
package moni.mybatis;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
public class Utils {
//读取xml为documnet对象
public static Document xml2Doc(InputStream in){
try {
DocumentBuilder documentBuilder=DocumentBuilderFactory.newInstance().newDocumentBuilder();
return documentBuilder.parse(in);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
//把字符串转换为字段对应的类型
public static Object changeType(String value,Class type)throws Exception{
if(type==byte.class || type==Byte.class){
return Byte.parseByte(value);
}
if(type==short.class || type==Short.class){
return Short.parseShort(value);
}
if(type==int.class || type==Integer.class){
return Integer.parseInt(value);
}
if(type==char.class || type==Character.class){
return value.indexOf(0);
}
if(type==long.class || type==Long.class){
return Long.parseLong(value);
}
if(type==float.class || type==Float.class){
return Float.parseFloat(value);
}
if(type==double.class || type==Double.class){
return Double.parseDouble(value);
}
if(type==boolean.class || type==Boolean.class){
return Boolean.parseBoolean(value);
}
if(type==Date.class){
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(value);
}
return value;
}
//获取mybatis中所有占位符的名字
public static List<String> parseMybatisSql(String sql){
List<String> names=new ArrayList<String>();
//) values(#{sname},#{sex},#{sdy},#{sbirth},#{score},#{sphoto})
int indexStart=0;
while(true){
indexStart=sql.indexOf("#{",indexStart);
if(indexStart==-1){
break;
}
int indexEnd=sql.indexOf("}",indexStart);
//截取子串
String name=sql.substring(indexStart+2, indexEnd);
names.add(name);
indexStart++;
}
return names;
}
}
- 核心配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<environments default="development">
<environment id="development">
<!-- 事务管理方式: -->
<transactionManager type="JDBC"/>
<!-- 定义数据源:指定连接数据库的四大参数 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/db_1?characterEncoding=utf-8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/zhiyou100/mapper/StudentMapper.xml"/>
</mappers>
</configuration>
- 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.zhiyou100.mapper.StudentMapper" >
<select id="getOne" parameterType="int" resultType="com.zhiyou100.entity.Student">
select * from student where sid=#{sid}
</select>
<select id="getAll" resultType="com.zhiyou100.entity.Student">
select * from student
</select>
<delete id="deleteOne" parameterType="int">
delete from student where sid=#{sid}
</delete>
<update id="updateOne" parameterType="com.zhiyou100.entity.Student">
update student set sname=#{sname},sex=#{sex},sage=#{sage},sdy=#{sdy},score=#{score} where sid=#{sid}
</update>
<insert id="addOne" parameterType="com.zhiyou100.entity.Student">
insert into student(sname,sex,sdy,sage,score) values(#{sname},#{sex},#{sdy},#{sage},#{score})
</insert>
</mapper>
- 创建一个类描述所有的sql标签
package moni.mybatis;
public class SqlElement {
private String eleName;
private String eleId;
private String parameterType;
private String resultType;
private String sql;
public String getEleName() {
return eleName;
}
public void setEleName(String eleName) {
this.eleName = eleName;
}
public String getEleId() {
return eleId;
}
public void setEleId(String eleId) {
this.eleId = eleId;
}
public String getParameterType() {
return parameterType;
}
public void setParameterType(String parameterType) {
this.parameterType = parameterType;
}
public String getResultType() {
return resultType;
}
public void setResultType(String resultType) {
this.resultType = resultType;
}
public String getSql() {
return sql;
}
public void setSql(String sql) {
this.sql = sql;
}
@Override
public String toString() {
return "SqlElement [eleName=" + eleName + ", eleId=" + eleId + ", parameterType=" + parameterType
+ ", resultType=" + resultType + ", sql=" + sql + "]";
}
public SqlElement(String eleName, String eleId, String parameterType, String resultType, String sql) {
super();
this.eleName = eleName;
this.eleId = eleId;
this.parameterType = parameterType;
this.resultType = resultType;
this.sql = sql;
}
}
6.5 创建sqlsessionfactory类
1 读取核心配置文件中连接数据库的四大参数
2 读取mapper映射文件:把所有的sql标签读成sqlElement对象
package moni.mybatis;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.nio.channels.FileLockInterruptionException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
public class SqlSessionFactory {
private InputStream in;
private static Map<String, String> jdbcMap;
private static String mapperPath;
public static Map<String, SqlElement> sqlElementMap;
public SqlSessionFactory(InputStream in) {
this.in = in;
Document doc=Utils.xml2Doc(in);
//获取连接数据库的四大参数
//1 获取<dataSource type="POOLED">标签
Element eleDS=(Element)doc.getElementsByTagName("dataSource").item(0);
jdbcMap=new HashMap<String, String>();
//2 获取其所有的property属性
NodeList propertyList=eleDS.getElementsByTagName("property");
for (int i = 0; i < propertyList.getLength(); i++) {
Element elePro=(Element)propertyList.item(i);
// 3 获取其name和value属性:<property name="driver" value="com.mysql.jdbc.Driver"/>
jdbcMap.put(elePro.getAttribute("name"), elePro.getAttribute("value"));
}
//获取sql映射文件所在的位置
// <mapper resource="com/zhiyou100/mapper/StudentMapper.xml"/>
mapperPath=((Element)doc.getElementsByTagName("mapper").item(0)).getAttribute("resource");
//注册驱动
try {
Class.forName(jdbcMap.get("driver"));
} catch (Exception e) {
throw new RuntimeException(e);
}
//读取此sql映射文件中的所有sql标签::装入sqlElementMap
sqlElementMap=new HashMap<String, SqlElement>();
try {
//获取的路径前面加src
mapperPath="src/"+mapperPath;
Document docSql=Utils.xml2Doc(new FileInputStream(mapperPath));
//获取其mapper标签
Element eleMapper=(Element)docSql.getElementsByTagName("mapper").item(0);
//获取其下的所有子标签
NodeList sqlEleList=eleMapper.getChildNodes();
for (int i = 0; i < sqlEleList.getLength(); i++) {
if(sqlEleList.item(i) instanceof Element){
Element sqlEleZi=(Element)sqlEleList.item(i);
//获取标签的所有信息 封装为SqlElement对象
String eleName=sqlEleZi.getNodeName();
String eleId=sqlEleZi.getAttribute("id");
String parameterType=sqlEleZi.getAttribute("parameterType");
String resultType=sqlEleZi.getAttribute("resultType");
String sql=sqlEleZi.getTextContent().trim();
sqlElementMap.put(eleId, new SqlElement(eleName, eleId, parameterType, resultType, sql));
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public SqlSession openSession() {
try {
//每个sqlsession对应一个数据库的连接
Connection con=DriverManager.getConnection(jdbcMap.get("url"), jdbcMap.get("username"), jdbcMap.get("password"));
return new SqlSession(con);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
6.6 创建sqlsession
执行所有的sql标签中的sql语句
package moni.mybatis;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class SqlSession {
private Connection con;
public SqlSession(Connection con){
this.con=con;
}
public void close(){
try {
con.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public Object selectOne(String id,Object...args){
List list=(List)executeSql(id, args);
return list.get(0);
}
public List selectList(String id,Object...args){return (List)executeSql(id, args);}
public int delete(String id,Object...args){return (Integer)executeSql(id, args);}
public int update(String id,Object...args){return (Integer)executeSql(id, args);}
public int insert(String id,Object...args){return (Integer)executeSql(id, args);}
private Object executeSql(String id,Object...args){
//通过id获取对应的sqlelement对象
SqlElement sqlElement=SqlSessionFactory.sqlElementMap.get(id);
//获取sql:中所有占位符的名字
List<String> names=Utils.parseMybatisSql(sqlElement.getSql());
//替换sql中的#{xxx}
String sql=sqlElement.getSql().replaceAll("#\\{[0-9a-zA-Z_$]+\\}", "?");
try {
//获取预编译对象
PreparedStatement sta=con.prepareStatement(sql);
//给占位符赋值:
if(names.size()==1){//如果说只有一个占位符:直接拿参数赋值即可
sta.setObject(1, args[0]);
}else if(names.size()>1){//如果说有多个占位符:拿参数对象的属性 赋值给对应的占位符
//获取字节码文件对象
Class cla=args[0].getClass();
for (int i=0;i<names.size();i++) {
Field field=cla.getDeclaredField(names.get(i));
field.setAccessible(true);
Object fieldValue=field.get(args[0]);
sta.setObject(i+1, fieldValue);
}
}
//执行sql
//如果是select 需要解析结果集
//如果是dml 需要获取影响的行数
if(sqlElement.getEleName().equals("select")){
ResultSet set=sta.executeQuery();
//把结果集封装为对象
List list=new ArrayList();
Class cla=Class.forName(sqlElement.getResultType());
while(set.next()){
Object obj=cla.newInstance();//每行要被解析成一个对象
Field[] fieldArr=cla.getDeclaredFields();
for (Field field : fieldArr) {
field.setAccessible(true);
field.set(obj, set.getObject(field.getName()));
}
list.add(obj);
}
set.close();
return list;
}else{
int hang=sta.executeUpdate();
sta.close();
return hang;
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
6.7 测试
package test01;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import com.zhiyou100.entity.Student;
import moni.mybatis.SqlSession;
import moni.mybatis.SqlSessionFactory;
public class Test01 {
public static void main(String[] args) throws Exception{
//1 加载核心配置文件 创建SqlSessionFactory对象
InputStream inputStream = new FileInputStream("src/mybatis_config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactory(inputStream);
//2 通过SqlSessionFactory对象和SqlSession对象
SqlSession sqlSession=sqlSessionFactory.openSession();
System.out.println("获取一个:"+sqlSession.selectOne("getOne", 1));
System.out.println("获取所有:"+sqlSession.selectList("getAll", null));
System.out.println("添加一个:"+sqlSession.insert("addOne", new Student(null, "模拟1", "妖", 100, true, 11f)));
System.out.println("删除一个:"+sqlSession.delete("deleteOne", 3));
System.out.println("修改一个:"+sqlSession.update("updateOne", new Student(4, "模拟4", "妖", 140, true, 44f)));
//5 关闭session
sqlSession.close();
}
}
6.8 改进 一次加载多个sql映射文件
- 创建teacher表
- 创建Teacher类
- 创建sql映射文件
- 在核心配置文件中引入此sql映射文件
- 更改SqlSessionFactory中读取mapper标签时 不再是一个 而是多个
//读取此sql映射文件中的所有sql标签::装入sqlElementMap
sqlElementMap=new HashMap<String, SqlElement>();
try {
for (String mapperPath : mapperPaths) {
//获取的路径前面加src
mapperPath="src/"+mapperPath;
Document docSql=Utils.xml2Doc(new FileInputStream(mapperPath));
//获取其mapper标签
Element eleMapper=(Element)docSql.getElementsByTagName("mapper").item(0);
//获取其下的所有子标签
NodeList sqlEleList=eleMapper.getChildNodes();
for (int i = 0; i < sqlEleList.getLength(); i++) {
if(sqlEleList.item(i) instanceof Element){
Element sqlEleZi=(Element)sqlEleList.item(i);
//获取标签的所有信息 封装为SqlElement对象
String eleName=sqlEleZi.getNodeName();
String eleId=sqlEleZi.getAttribute("id");
String parameterType=sqlEleZi.getAttribute("parameterType");
String resultType=sqlEleZi.getAttribute("resultType");
String sql=sqlEleZi.getTextContent().trim();
sqlElementMap.put(eleId, new SqlElement(eleName, eleId, parameterType, resultType, sql));
}
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
- 测试
package test01;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import com.zhiyou100.entity.Student;
import com.zhiyou100.entity.Teacher;
import moni.mybatis.SqlSession;
import moni.mybatis.SqlSessionFactory;
public class Test01 {
public static void main(String[] args) throws Exception{
//1 加载核心配置文件 创建SqlSessionFactory对象
InputStream inputStream = new FileInputStream("src/mybatis_config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactory(inputStream);
//2 通过SqlSessionFactory对象和SqlSession对象
SqlSession sqlSession=sqlSessionFactory.openSession();
System.out.println("对student进行增删改查:::");
System.out.println("获取一个:"+sqlSession.selectOne("getOneStudent", 1));
System.out.println("获取所有:"+sqlSession.selectList("getAllStudent", null));
System.out.println("添加一个:"+sqlSession.insert("addOneStudent", new Student(null, "模拟2", "妖", 100, true, 11f)));
System.out.println("删除一个:"+sqlSession.delete("deleteOneStudent", 5));
System.out.println("修改一个:"+sqlSession.update("updateOneStudent", new Student(6, "模拟6", "妖", 140, true, 44f)));
System.out.println("对Teacher进行增删改查:::");
System.out.println("获取一个:"+sqlSession.selectOne("getOneTeacher", 1));
System.out.println("获取所有:"+sqlSession.selectList("getAllTeacher", null));
System.out.println("添加一个:"+sqlSession.insert("addOneTeacher", new Teacher(null, "校老师", 34, 10000f, "男")));
System.out.println("删除一个:"+sqlSession.delete("deleteOneTeacher", 3));
System.out.println("修改一个:"+sqlSession.update("updateOneTeacher", new Teacher(2, "田老师", 32, 20000f, "妖")));
//5 关闭session
sqlSession.close();
}
}
- 所有sql映射文件的sql标签的id必须唯一
7 模拟mybatis的mapper代理方式
7.1 创建sql映射文件对应的类:
技术能力有限:不能通过接口获取代理对象 只能通过类获取其代理对象
package com.zhiyou100.mapper;
import java.util.List;
import com.zhiyou100.entity.Student;
//动态代理还是cglib代理 都需要指定目标对象:::目前无法实现由接口获取一个接口的实现类对象
public class StudentMapper {
// <select id="getOneStudent" parameterType="int" resultType="com.zhiyou100.entity.Student">
// select * from student where sid=#{sid}
// </select>
public Student getOneStudent(int sid){return null;}
// <select id="getAllStudent" resultType="com.zhiyou100.entity.Student">
// select * from student
// </select>
public List<Student> getAllStudent(){return null;}
// <delete id="deleteOneStudent" parameterType="int">
// delete from student where sid=#{sid}
// </delete>
public int deleteOneStudent(int sid){return 0;}
// <update id="updateOneStudent" parameterType="com.zhiyou100.entity.Student">
// update student set sname=#{sname},sex=#{sex},sage=#{sage},sdy=#{sdy},score=#{score} where sid=#{sid}
// </update>
public int updateOneStudent(Student s){return 0;}
// <insert id="addOneStudent" parameterType="com.zhiyou100.entity.Student">
// insert into student(sname,sex,sdy,sage,score) values(#{sname},#{sex},#{sdy},#{sage},#{score})
// </insert>
public int addOneStudent(Student s){return 0;}
}
7.2 在sqlsessioon中添加方法getMapper
getMapper方法中要获取指定类的代理对象:对目标对象的方法进行改进:去调用方法名对应的sql标签
public Object getMapper(Class cla)throws Exception{
Object obj=cla.newInstance();
class ProxyFactroy03 implements MethodInterceptor{
//4 定义引用 记录被代理对象
private Object target;
//通过方法给被代理对象引用赋值
public void setTarget(Object obj){
this.target=obj;
}
//5 创建方法获取代理对象:::被代理对象的子类对象
public Object newInstance(){
//工具类
Enhancer en = new Enhancer();//创建一个增强工具类对象
//设置父类
en.setSuperclass(target.getClass());
//设置回调函数的对象
en.setCallback(this);
//创建代理对象---是目标类的子类对象
return en.create();
}
//6 代理对象的方法实现对被代理对象功能的控制
//参数1:arg0代理对象
//参数2:method被代理对象的方法
//参数3:arg2被代理对象的方法的参数列表
//参数4:arg3被代理对象方法的代理
public Object intercept(Object arg0, Method method, Object[] arg2, MethodProxy arg3) throws Throwable {
//执行method对应的sql标签
//获取方法名
String methodName=method.getName();
Object result=executeSql(methodName, arg2);
System.out.println(method.getReturnType()+"::"+result.getClass());
if (!(method.getReturnType()==int.class||method.getReturnType()==Integer.class||method.getReturnType()==List.class||method.getReturnType()==ArrayList.class)) {
List list=(List)result;
result=list.get(0);
}
return result;
}
}
ProxyFactroy03 factroy=new ProxyFactroy03();
factroy.setTarget(obj);
return factroy.newInstance();
}
7.3 测试
package test01;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import com.zhiyou100.entity.Student;
import com.zhiyou100.entity.Teacher;
import com.zhiyou100.mapper.StudentMapper;
import moni.mybatis.SqlSession;
import moni.mybatis.SqlSessionFactory;
public class Test02 {
public static void main(String[] args) throws Exception{
//1 加载核心配置文件 创建SqlSessionFactory对象
InputStream inputStream = new FileInputStream("src/mybatis_config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactory(inputStream);
//2 通过SqlSessionFactory对象和SqlSession对象
SqlSession sqlSession=sqlSessionFactory.openSession();
StudentMapper studentMapper=(StudentMapper)sqlSession.getMapper(StudentMapper.class);
System.out.println("获取一个:"+studentMapper.getOneStudent(1));
System.out.println("获取所有:"+studentMapper.getAllStudent());
System.out.println("添加一个:"+studentMapper.addOneStudent( new Student(null, "模拟777777", "妖", 100, true, 11f)));
System.out.println("删除一个:"+studentMapper.deleteOneStudent(4));
System.out.println("修改一个:"+studentMapper.updateOneStudent(new Student(6, "模拟666666", "妖", 100, true, 11f)));
sqlSession.close();
}
}