在学习Mybatis原理之前我们先来看这样的jdbc代码
@Test
public void JdbcTest() {
// 声明连接对象
Connection conn = null;
// 声明预处理对象
PreparedStatement prep = null;
// 声明结果集对象
ResultSet result = null;
try {
// 加载数据库驱动
Class.forName("com.mysql.jdbc.Driver");
// 创建数据库连接
conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/mydb?charset=utf8&serverTimezone=UTC&useSSL=false", "root", "root");
// 创建sql语句
String sql = "select * from Celebrity where c_id=?";
// 预处理Sql语句
prep = conn.prepareStatement(sql);
// 用过预处理对象,设置sql语句参数
prep.setString(1, "10");
// 执行sql语句返回结果集
result = prep.executeQuery();
// 处理结果集数据
while (result.next()) {
System.out.println(result.getString("c_id") + " " + result.getString("c_name"));
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 关闭结果集资源
if (result != null) {
try {
result.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
// 关闭预处理对象资源
if (prep != null) {
try {
prep.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
// 关闭连接对象资源
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
在实际开发过程中我们很快就会发现原始JDBC的不足之处,
1.每次访问数据库都需要进行数据库连接对象的创建和关闭操作,占用了数据库连接的资源,影响了数据库的性能。
解决: 使用数据库连接池
2.数据库的操作语句使用了硬编码,导致(需求改变时)每次修改sql语句都需要重新编译java代码,不利于系统维护
解决:将sql语句抽取出来,形成一个xml配置文件,需求改变时,开发人员只需要专注于配置文件,不需要管java代码。
3.数据库操作语句的设定参数使用了硬编码
解决:PreparedStatement将sql语句占位符和参数,配置在xml配置文件内
4.结果集的处理存在硬编码,不利于维护
解决:将结果集(ResultSet)里面的数据直接和java对象映射在一起,这样就可以直接获取java对象
然后我们参考Mybatis的执行流程:
1. 配置MybatisConfig.配置文件,设置Mybatis环境配置DataSource数据源,配置ORM映射配置文件mapper.xml
2.加载MybatisConfig配置文件,创建SqlSessionFactory工厂对象
3.通过工厂对象factory创建sqlSession会话对象
4.在sqlSession会话对象方法内部 通过Configuration配置对象创建MappedStatement对象
MappedStatement ms = configuration.getMappedStatement(statement);根据key(namespace+tagId)获取MappedStatement对象。
MappedStatement会创建一个Cache对象,并指定唯一的key(由sql,mappedStatement,parameter,rowbound(用于分页)联合生成,具有唯一性),用于缓存数据库操作(涉及mybatis相关的一级缓存操作),当下次执行相同的操作时,通过检查Cache对象的key,如果key相同则表明是相同的数据库操作,则直接拿Cache里面的结果返回
5.通过内置的executer执行数据库相关操作
executer.query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
获取Cache,Cache为空,通过BaseExecutor执行数据库操作,
Cache不为空,存在缓存,通过CachingExecutor获取结果(从缓存中拿取)
补充,#{}与${}的区别:
1.#{}表示占位符
2.${}表示拼接sql串,将接收到的参数不加如何修饰直接拼接到sql语句中。
注意:但是这样也存在风险,可能引发sql注入(不建议使用)。
${}只能使用value参数名来接收输入参数(java简单类型),例子:select * from user where u_name like '%${value}%'
Mybatis完成增删改查操作(非注解)
1.查询(通过主键查询)
<!--标签的id属性起到标识标签的作用,同时由于mybatis框架会将sql语句封装到一个MappedStatement底层对象中,此id也就相当于MappedStatement对象的id -->
<!--1.将SQL语句侧参数通过占位符(#{})提交到动态提交到sql语句中 -->
<!--#{}表示一个占位符 -->
<!--2.将sql语句的返回结果和java实体对象映射 -->
<!--3.parameterType:表示sql语句输入参数的类型,注意如果参数为简单类型,则占位符参数名可以任意定义 -->
<!--4.resultType:表示sql语句执行结果所映射的java对象的类型 (单条记录) -->
<select id="getById" parameterType="int"
resultType="com.ncs.entity.Celebrity">
select * from celebrity where c_id=#{value}
</select>
2.查询(非主键模糊查询)
<!--注意:resultType:指定的就是单条记录所映射的java对象 -->
<!--${}表示拼接sql语句,将接收到的参数不加任何修饰直接拼接到sql语句中 -->
<!--注意:但是这样存在sql注入风险 ,而且当使用${}拼接sql时,只能通过参数名value来接收参数,其他参数名定义不行。 -->
<select id="getByName" parameterType="string"
statementType="STATEMENT" resultType="com.ncs.entity.Celebrity">
select * from celebrity where
c_name like '%${value}%'
</select>
3.添加(mysql自增长主键) //由于底层conn.setAutoCommit(false)设置为非自动提交,涉及数据一致性,所以用户必须手动提交来保证数据一致性。
<!--mysql自增主键 -->
<insert id="insertCelebrity"
parameterType="com.ncs.entity.Celebrity">
<!--如果使用自增长的主键,且需要返回自增长的主键属性值给到原来保存成功的对象上 -->
<selectKey keyProperty="c_id" order="AFTER" resultType="java.lang.Integer">
select LAST_INSERT_ID()
</selectKey>
insert into celebrity(c_id,c_name) values(#{c_id},#{c_name})
</insert>
4.添加(mysql非自增长主键(uuid))
<!--mysql非自增主键(uuid) -->
<insert id="insertCelebrityWithUUID"
parameterType="com.ncs.entity.Celebrity">
<!--如果使用mysql uuid,且需要返回自增长的主键属性值给到原来保存成功的对象上 -->
<selectKey keyProperty="c_id" order="BEFORE" resultType="java.lang.String">
select uuid()
</selectKey>
insert into celebrity(c_id,c_name) values(#{c_id},#{c_name})
</insert>
5.添加(Oracle非自增长(序列))
<!--oracle非自增主键(sequence) -->
<insert id="insertCelebrityWithSequence"
parameterType="com.ncs.entity.Celebrity">
<!--使用序列值,且需要返回自增长的主键属性值给到原来保存成功的对象上 -->
<selectKey keyProperty="c_id" order="BEFORE" resultType="java.lang.String">
select SequenceName.nextval()
</selectKey>
insert into celebrity(c_id,c_name) values(#{c_id},#{c_name})
</insert>
6.删除 (由于底层conn.setAutoCommit(false)设置为非自动提交,涉及数据一致性,所以用户必须手动提交来保证数据一致性。)
<!--由于mybatis底层conn.setAutoCommit(false),表示涉及数据一致性的操作需要用户手动提交 -->
<delete id="deleteCelebrityById" parameterType="int">
delete from celebrity where c_id=#{c_id}
</delete>
7.更新
<!--由于mybatis底层conn.setAutoCommit(false),表示涉及数据一致性的操作需要用户手动提交 -->
<update id="updateCelebrity" parameterType="com.ncs.entity.Celebrity">
update celebrity set c_name=#{c_name} where c_id=#{c_id}
</update>