1.JDBC的缺陷是什么?
1.代码冗余。加载驱动,创建连接对象,数据库操作对象,关闭对象等操作对应每个数据库操作都是一样的。
2.对应ResultSet结果集,我们要遍历他然后手工创建Java对象装入集合,比较麻烦。
3.数据库操作和业务代码混合。
2.Mybatis是什么?为什么要放弃JDBC而使用Mybatis?
Mybatis可以看做增强版jdbc,底层也是用jdbc操作数据库但Mybatis帮我们减轻了开发的复旦。不需要我们重复编写创建Connection,Statement,PreparedStatement,ResultSet对象的代码。不需我们关注这些对象的关闭,自动帮我们关闭。还有自动将ResultSet中的记录封装成java对象的集合返回给我们。让开发者专注于SQL语句的编写。
3.传统Dao方式使用Mybatis操作数据库的基本步骤(未使用动态代理为我们创建Dao接口实现类对象)
1.加入依赖:mybatis依赖,数据库驱动。
2.创建Dao接口,定义操作数据库的方法。
3.创建Dao接口的同名Sql映射文件(Mapper文件)。
4.创建Dao接口实现类:方法中获得SqlSession对象操作数据库。
5.创建主配置文件:配置数据库数据源,mapper文件的位置等。
public interface StudentDao {
List<Student> findAll();
}
<?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.zhaojisu.dao.StudentDao">
<select id="findAll" resultType="com.zhaojisu.entity.Student">
select name,age from student;
</select>
</mapper>
public class StudentDaoImpl implements StudentDao {
@Override
public List<Student> findAll() {
String path="mybatis.xml";
InputStream stream = Thread.currentThread().getContextClassLoader().getResourceAsStream(path);
SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(stream);
SqlSession sqlSession = factory.openSession();
List<Student> list = sqlSession.selectList("com.zhaojisu.dao.StudentDao.findAll");
sqlSession.close();
return list;
}
}
public class test {
@Test
public void testSelectAsList(){
StudentDao studentDao=new StudentDaoImpl();
List<Student> all = studentDao.findAll();
all.forEach(a->{
System.out.println(a);
});
}
}
4.SqlSessionFactoryBuilder类 SqlSessionFactory 接口 SqlSession 接口
SqlSessionFactoryBuilder类 :SqlSessionFactory 的 创 建 , 需 要 使 用 SqlSessionFactoryBuilder 对 象 的 build() 方 法 。 由 于 SqlSessionFactoryBuilder 对象在创建完工厂对象后,就完成了其历史使命,即可被销毁。所以,一般会将 该 SqlSessionFactoryBuilder 对象创建为一个方法内的局部对象,方法结束,对象销毁。
SqlSessionFactory 接口:SqlSessionFactory 接口对象是一个重量级对象(系统开销大的对象),是线程安全的,所以一个应用 只需要一个该对象即可。创建 SqlSession 需要使用 SqlSessionFactory 接口的的 openSession()方法。
➢ openSession(true):创建一个有自动提交功能的 SqlSession
➢ openSession(false):创建一个非自动提交功能的 SqlSession,需手动提交
➢ openSession():同 openSession(false)
SqlSession 接口:SqlSession接口对象用于对数据库进行操作。一个SqlSession对应一次数据库会话,一次会话以SqlSession的创建开始,以SqlSession的关闭结束。SqlSession不是线程安全的,每次使用完都需要关闭它。
5. 传统 Dao 开发方式的分析
从上面我们自己写的Dao接口实现类中发现,实现类其实没有干什么实质性的工作,核心就通过SqlSession接口的API,根据mapper文件中的namespace和id定位某条sql语句操作数据库。若Dao接口中有多个方法,实现类方法中大部分代码都是冗余的,处理SqlSession调用的API不同。所以可以使用Myabtis动态代理方式为我们创建Dao接口的实现类对象,省略了Dao接口实现类的编写过程,让开发人员关注Mapper文件中Sql语句的编写。
6. SqlSession.getMapper(Class<T> type)
public class AppTest {
@Test
public void t() {
InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream("mybatis.xml");
SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
SqlSession sqlSession = factory.openSession();
StudentDao mapper = sqlSession.getMapper(StudentDao.class);
System.out.println(mapper);
List<Student> all = mapper.findAll();
all.forEach(a->{
System.out.println(a);
});
}
}
方法返回Dao接口实现类的对象。类型为org.apache.ibatis.binding.MapperProxy。采用的是jdk动态代理的方式。mybatis根据传入的Dao接口的字节码,匹配所以mapper文件中的namespace。然后根据mapper文件中的sql语句创建Dao接口的实现类对象。
7.myabtis为我们创建Dao接口实现类的要求
1.Mapper文件中<mapper>标签中的namespace为Dao接口的全限定类名。
2.Mapper文件中<select><update><insert><delete>标签中id为Dao接口的方法名。
3.Dao接口中不能有重载方法的定义。
4.Dao接口名和Mapper文件名最好一致,方便我们自己对应。
8.mybatis传参
1.一个普通类型的参数:#{任意名}
接口中的方法:Student findOne(String name);
sql:<select id="findOne" resultType="com.zhaojisu.entity.Student">
select * from student where name=#{aaaa}
</select>
2.多个参数:使用@Param注解
接口中的方法:Student findOne2(@Param("name") String name, @Param("age") Integer age);
sql:<select id="findOne2" resultType="com.zhaojisu.entity.Student">
select * from student where name=#{name} and age=#{age}
</select>
3.多个参数:使用Java对象传参 #{对象的属性名}
接口中的方法:Student findOne3(Student student);
sql:<select id="findOne3" resultType="com.zhaojisu.entity.Student">
select * from student where name=#{name} and age=#{age}
</select>
4.多个参数,且参数有java对象,普通类型等:使用@Param注解
接口中的方法:List<Student> selectOne(@Param("age") Integer age, @Param("stu") Student student);
sql:<select id="selectOne" resultType="com.zhaojisu.entity.Student">
select * from t_student where age>#{age} or name=#{stu.name};
</select>
5.按位传参:#{arg0} #{arg1}.....
6.Map<String,Object>集合传参: #{key}
9. ${}
${}表示字符串替换。使用的是Statement对象。有sql注入的风险。通常用来替换表名或者列名。
10. #与$的区别 面试题
1.#是站位符,底层采用的是PreparedStatement。#{}编译之后变成?。效率高。
2.使用#避免了sql注入,比较安全。
3.$表示字符串替换,底层采用的是Statement。先将${}表示的字符串拼接然后执行sql。效率低。
4.使用$有sql注入的风险,在保证安全的情况下,有时需要进行sql注入。
5.$经常用来替换表名,列名。
11.resultType
返回的java对象类型。mybatis在封装结果时,先调用类的无参构造然后将查出的字段的值赋给同名属性。没有则不赋值。
12. resultMap
用于当表中字段与类的属性名不一致时,手动将字段对应属性。
<resultMap id="stu" type="com.zhaojisu.entity.Student"> <id column="name1" property="name"/> <result column="age1" property="age"/> </resultMap>主键字段用id标签,其他用result标签
13 动态Sql:使用动态Sql时,传入的参数要用java对象
<if test=""> </if>
<select id="selectStudentIf" resultType="com.bjpowernode.domain.Student">
select id,name,email,age from student where 1=1
<if test="name != null and name !='' ">
and name = #{name}
</if>
<if test="age > 0 ">
and age > #{age}
</if>
</select>if容易造成语法错误,所以在后面加一个不影响查询结果且恒成立的条件。
<where><if></if>....</where>
<select id="selectStudentWhere" resultType="com.bjpowernode.domain.Student">
select id,name,email,age from student
<where>
<if test="name != null and name !='' ">
and name = #{name}
</if>
<if test="age > 0 ">
and age > #{age}
</if>
</where>
</select>使用where标签,在有查询条件时,可以自动添加上 where 子句;没有查询条件时,不会添加 where 子句。需要注意的是,第一个if标签中的 SQL 片断,可以不包含 and。不过,写上 and 也不错, 系统会将多出的 and 去掉。但其它中 SQL 片断的 and,必须要求写上。否则 SQL 语句将拼接出错。
<foreach collection="" item="" open="" close="" spearator="">
</foreach>
<select id="selectStudentForList"
resultType="com.bjpowernode.domain.Student">
select id,name,email,age from student
<if test="list !=null and list.size > 0 ">
where id in
<foreach collection="list" open="(" close=")"
item="stuid" separator=",">
#{stuid}
</foreach>
</if>
</select>foreach标签一般用于in子句。
collection表示参数中的数组或List集合。
item表示数组或集合中的一个元素。
open表示循环开始前的字符
close表示循环结束后的字符
spearator表示元素之间的字符。
<sql></sql>用于sql语句的复用
14.PageHelper
PageHelper.startPage(PageNum,PageSize);分页效果只对该方法执行后的第一条查询产生影响。
PageHelper分页过程:
先count(*)计算查询出来的全部记录数,然后在sql语句后面加入limit ?,?进行分页。
15.mybatis缓存
一级缓存
一级缓存是SqlSession级别的,称为本地缓存。默认开启。在同一次会话中使用mybatis会缓存一条查询语句的结果。如果在会话内使用相同的查询语句会直接到缓存里面得到结果。通常,一个事务可视为一个会话。
二级缓存
在mapper文件中使用<cache type=""/>标签。type 属性指定的类必须实现 org.apache.ibatis.cache.Cache 接口,且提供一个接受 String 参数作为 id 的构造器。
public interface Cache { String getId(); int getSize(); void putObject(Object key, Object value); Object getObject(Object key); boolean hasKey(Object key); Object removeObject(Object key); void clear(); }
select语句自动加入缓存调用Cache接口的putObject方法,inset update delete自动删除缓存调用Cache接口的Clear方法。
16.cache-ref
对某一命名空间的语句,只会使用该命名空间的缓存进行缓存或刷新。 但你可能会想要在多个命名空间中共享相同的缓存配置和实例。要实现这种需求,你可以使用 cache-ref 元素来引用另一个缓存。
如<cache-ref namespace="com.someone.application.data.SomeMapper"/>
17.insert update返回自增主键的值
insert update标签中有userGeneratedKeys keyProperty keyColum属性
userGeneratedKeys表示是否取出数据库内部自增生成的主键。
keyProperty指定将取出的主键值赋给传入对象的哪个属性。
keyColumn指定哪个字段是主键。
insert,update操作的参数必须是java对象。
插入一行
<insert id="insertAuthor" useGeneratedKeys="true"
keyProperty="id">
insert into Author (username,password,email,bio)
values (#{username},#{password},#{email},#{bio})
</insert>
插入多行 传入一个 Author
数组或集合,并返回自动生成的主键。
<insert id="insertAuthor" useGeneratedKeys="true"
keyProperty="id">
insert into Author (username, password, email, bio) values
<foreach item="item" collection="list" separator=",">
(#{item.username}, #{item.password}, #{item.email}, #{item.bio})
</foreach>
</insert>