61、什么是Mybatis以及优缺点和适用场景
一:简介: mybatis是一个开源免费框架,原名是ibatis,现存于github中。 mybatis封装了几乎所有的JDBC代码。 二:mybatis的优点:
与JDBC相比,减少了50%以上的代码量。
MyBatis是最简单的持久化框架,小巧并且简单易学。
MyBatis相当灵活,不会对应用程序或者数据库的现有设计强加任何影响,SQL写在XML里,从程序代码中彻底分离,降低耦合度,便于统一管理和优化,并可重用。
提供XML标签,支持编写动态SQL语句。
提供映射标签(resultMap),支持对象与数据库的ORM字段关系映射。
三、MyBatis框架的缺点:
SQL语句的编写工作量较大,尤其是字段多、关联表多时,更是如此,对开发人员编写SQL语句的功底有一定要求。
SQL语句依赖于数据库,导致数据库移植性差,不能随意更换数据库。
四、MyBatis框架适用场合:
MyBatis专注于SQL本身,是一个足够灵活的DAO层解决方案。
对性能的要求很高,或者需求变化较多的项目,如互联网项目,MyBatis将是不错的选择。
62、MyBatis 与 Hibernate 有哪些不同?为什么说 Mybatis 是半自动 ORM 映射工具?它与全自动的区别在哪里
-
hibernate 是全自动,而 mybatis 是半自动。 hibernate 完全可以通过对象关系模型实现对数据库的操作,拥有完整的 JavaBean 对象与数据库的映射结构来自动生成 sql。而 mybatis 仅有基本的字段映射,对象数据以及对象实际关系仍然需要通过手写 sql 来实现和管理。
-
hibernate 数据库移植性远大于 mybatis。
hibernate 通过它强大的映射结构和 hql 语言,大大降低了对象与数据库(oracle、mysql 等)的耦合性,而 mybatis 由于需要手写 sql,因此与数据库的耦合性直接取决于程序员写 sql 的方法,如果 sql 不具通用性而用了很多某数据库特性的 sql 语句的话,移植性也会随之降低很多,成本很高。
-
hibernate 拥有完整的日志系统,mybatis 则欠缺一些。
hibernate 日志系统非常健全,涉及广泛,包括:sql 记录、关系异常、优化警告、缓存提示、脏数据警告等;而 mybatis 则除了基本记录功能外,功能薄弱很多。
-
mybatis 相比 hibernate 需要关心很多细节
hibernate 配置要比 mybatis 复杂的多,学习成本也比 mybatis 高。但也正因为 mybatis 使用简单,才导致它要比 hibernate 关心很多技术细节。mybatis 由于不用考虑很多细节,开发模式上与传统 jdbc 区别很小,因此很容易上手并开发项目,但忽略细节会导致项目前期 bug 较多,因而开发出相对稳定的软件很慢,而开发出软件却很快。hibernate 则正好与之相反。但是如果使用 hibernate 很熟练的话,实际上开发效率丝毫不差于甚至超越 mybatis。
-
sql 直接优化上,mybatis 要比 hibernate 方便很多
由于 mybatis 的 sql 都是写在 xml 里,因此优化 sql 比 hibernate 方便很多。而 hibernate 的 sql 很多都是自动生成的,无法直接维护 sql;虽有 hql,但功能还是不及 sql 强大,见到报表等变态需求时,hql 也歇菜,也就是说 hql 是有局限的;hibernate 虽然也支持原生 sql,但开发模式上却与 orm 不同,需要转换思维,因此使用上不是非常方便。总之写 sql 的灵活度上 hibernate 不及 mybatis。
总结: Mybatis 是半 orm 框架,主要侧重于 SQL 编写。对数据库依赖比较强,不便于移植。 Hibernate 是全自动化 orm 框架,只需要关注对象,性能方面比较差些。
Hibernate 属于全自动 ORM 映射工具,使用 Hibernate 查询关联对象或者关联集合对象时,可以根据对象关系模型直接获取,所以它是全自动的。而 Mybatis 在查询关联对象或关联集合对象时,需要手动编写 sql 来完成,所以,称之为半自动 ORM 映射工具。
63、#{}和${}的区别是什么?
1)#{}是预编译处理,$ {}是字符串替换。
2)mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值;mybatis在处理 $ { } 时,就是把 ${ } 替换成变量的值。
3)使用 #{} 可以有效的防止SQL注入,提高系统安全性。
项目实战中使用,请阅读我博客中Java项目实战分类中的--MySQL中in('5,6,7')只取第一个id为5对应的数据的思考一文,谢谢。
要理解记忆这个题目,我觉得要抓住两点:
(1)$ 符号一般用来当作占位符,常使用Linux脚本的同学应该对此有更深的体会吧。既然是占位符,当然就是被用来替换的。知道了这点就能很容易区分$和#,从而不容易记错了。
(2)预编译的机制。预编译是提前对SQL语句进行预编译,而其后注入的参数将不会再进行SQL编译。我们知道,SQL注入是发生在编译的过程中,因为恶意注入了某些特殊字符,最后被编译成了恶意的执行操作。而预编译机制则可以很好的防止SQL注入。在某些特殊场合下只能用${},不能用#{}。例如:在使用排序时ORDER BY ${id},如果使用#{id},则会被解析成ORDER BY “id”,这显然是一种错误的写法。
64、 模糊查询 like 语句该怎么写?
在 java 中拼接通配符,通过#{}赋值
string wildcardname = “%smi%”; list<name> names = mapper.selectlike(wildcardname);
select * from foo where bar like #{value}
在 Sql 语句中拼接通配符 (不安全,会引起 Sql 注入)
string wildcardname = “smi”; list<name> names = mapper.selectlike(wildcardname); ---------------------------------------------------------- <select id="selectlike"> select * from foo where bar like "%"#{value}"%" </select>
65、通常一个 Xml 映射文件,都会写一个 Dao 接口与之对应,请问,这个 Dao 接口的工作原理是什么?Dao 接口里的方法,参数不同时,方法能重载吗?
不能重载,因为通过 Dao 寻找 Xml 对应的 sql 的时候全限名+方法名的保存和寻找策略。接口工作原理为 jdk动态代理原理,运行时会为 dao 生成 proxy,代理对象会拦截接口方法,去执行对应的 sql 返回数据。
==============================================================================
1.DAO接口的实现原理 答:Dao的实现原理,是Mybatis框架通过JDK动态代理接口,当调用接口方法时,代理拦截调用,并通过接口全限名+方法名组成key的方式,查找XML映射文件中的MapperStatement,返回执行SQL后的结果
2.DAO接口的方法参数不同时,可以被重载吗? 答:DAO接口的方法在JDK代理层面可以被重载。对于default修饰的方法mybatis会直接代理执行,而不是指向MapperStatement,所以重载default方法是有效的。
66、Mybatis 是如何进行分页的?分页插件的原理是什么?
一、mybatis的4种分页方式(物理分页、逻辑分页) 1、借助Sql语句Q进行分页(物理分页)
2、拦截器分页(物理分页)通过拦截器给sq语句末尾加Eimt语句来查询
3、借助 数组Q进行分页(逻辑分页)
4、RowBounds分页插件实现分页(逻辑分页)
二、mybatis分页的原理 mybatis分页原理是:在MyBatis内部定义了一个拦截器接口,其中一个关键的方法就是intercept,从而实现拦截
public interface Interceptor { 0bject intercept(Invocation invocation) throws Throwable;Object plugin(object target); void setProperties(Properties properties); }
分页插件的原理就是使用MyBatis提供的插件接口,实现自定义插件,在插件的拦截方法内,拦截待执行的SQL,然后根据设置的dialect(方言),和设置的分页参数,重写SQL ,生成带有分页语句的SQL,执行重写后的SQL,从而实现分页
67、Mybatis 是如何将 sql 执行结果封装为目标对象并返回的?都有哪些映射形式?
第一种是使用标签,逐一定义列名和对象属性名之间的映射关系。
第二种是使用 sql列的别名功能,将列别名书写为对象属性名,比如 T_NAME AS NAME,对象属性名一般是name,小写,但是列名不区分大小写,Mybatis 会忽略列名大小写,智能找到与之对应对象属性名,你甚至可以写成 T_NAME AS NaMe,Mybatis 一样可以正常工作。 有了列名与属性名的映射关系后,Mybatis通过反射创建对象,同时使用反射给对象的属性逐一赋值并返回,那些找不到映射关系的属性,是无法完成赋值的。
68、如何执行批量插入?
首先,创建一个简单的 insert 语句:
<insert id="insertname"> insert into names (name) values (#{value}) </insert
然后在 java 代码中像下面这样执行批处理插入:
list < string > names = new arraylist(); names.add(“fred”); names.add(“barney”); names.add(“betty”); names.add(“wilma”); // 注意这里 executortype.batch sqlsession sqlsession = sqlsessionfactory.opensession(executortype.batch); try { namemapper mapper = sqlsession.getmapper(namemapper.class); for (string name: names) { mapper.insertname(name); } sqlsession.commit(); } catch (Exception e) { e.printStackTrace(); sqlSession.rollback(); throw e; } finally { sqlsession.close(); }
69、如何获取自动生成的(主)键值?
insert方法总是返回一个int值, 这个值代表的是插入的行数。
如果采用自增长策略,自动生成的键值在insert方法执行完后可以被设置到传入的参数对象中。
示例:
<insert id=" insertname”usegeneratedkeys=" true”keyproperty=“id” > insert into names (name) values (#{name)) name name = new name0; name.setname( “fred” ); int rows = mapper.insertname(name);
70、在 mapper 中如何传递多个参数?
1、第1种:直接使用参数名
//DAO public List<Student> selectLike(String name,Integer gender); <select id="selectLike" resultMap="BeanResultMap"> SELECT <include refid="All_Columns" /> FROM student WHERE username like "%"#{name}"%" AND gender = #{gender} </select>
2、第2种:#{param1}、#{param2} 获取参数
param1代表第一个参数,param2代表第二个参数。更多参数依次类推。
//DAO public List<Student> selectLike(String name,Integer gender); <select id="selectLike" resultMap="BeanResultMap"> SELECT <include refid="All_Columns" /> FROM student WHERE username like "%"#{param1}"%" AND gender = #{param2} </select>
3、第3种,使用 @Param 注解,重命名参数
//DAO public List<Student> selectLike(@Param("username") String name, Integer gender); <select id="selectLike" resultMap="BeanResultMap"> SELECT <include refid="All_Columns" /> FROM student WHERE username like "%"#{username}"%" AND gender = #{gender} </select>
4、第4种,可以将多个参数封装成对象或HashMap来传递
71、Mybatis 动态 sql 有什么用?执行原理?有哪些动态 sql?
-
Mybatis 动态 sql 可以让我们在 Xml 映射文件内,以标签的形式编写动态 sql,完成逻辑判断和动态拼接 sql 的功能。
-
Mybatis 提 供 了 9 种 动 态 sql 标 签 : trim|where|set|foreach|if|choose|when|otherwise|bind。
-
其执行原理为,使用 OGNL 从 sql 参数对象中计算表达式的值,根据表达式的值动态拼接 sql,以此来完成动态 sql 的功能。
72、Xml 映射文件中,除了常见的 select|insert|updae|delete标签之外,还有哪些标签?
<cache/>标签,给定命名空间的缓存配置。
<cache-ref/>标签,其他命名空间缓存配置的引用。
<resultMap/>标签,是最复杂也是最强大的元素,用来描述如何从数据库 结果集中来加载对象。
<parameterMap/>标签,已废弃!老式风格的参数映射。内联参数是首选, 这个元素可能在将来被移除,这里不会记录。
<sql/>标签,可被其他语句引用的可重用语句块。
<include/>标签,引用 标签的语句。
<selectKey/>标签,不支持自增的主键生成策略标签。
73、Mybatis 的 Xml 映射文件中,不同的 Xml 映射文件,id 是否可以重复?
不同的Xml映射文件,如果配置了namespace,那么id可以重复;如果没有配置namespace,那么id不能重复;毕竟namespace不是必须的,只是最佳实践而已;
原因就是namespace+id是作为Map的key使用的,如果没有namespace,就剩下id,那么,id重复会导致数据互相覆盖;有了namespace,自然id就可以重复,namespace不同,namespace+id自然也就不同;
74、MyBatis 实现一对一有几种方式?具体怎么操作的?
有联合查询和嵌套查询,联合查询是几个表联合查询,只查询一次,通过在 resultMap 里面配置 association节点配置一对一的类就可以完成;嵌套查询是先查一个表,根据这个表里面的结果的外键 id,去再另外一个表里面查询数据,也是通过 association 配置,但另外一个表的 查询通过 select 属性配置。
75、MyBatis 实现一对多有几种方式,怎么操作的?
有联合查询和嵌套查询。联合查询是几个表联合查询,只查询一次,通过在resultMap 里面的 collection 节点配置一对多的类就可以完成;嵌套查询是先查一个表,根据这个表里面的 结果的外键 id,去再另外一个表里面查询数据,也是通过配置 collection,但另外一个表的查询通过 select 节点配置。
76、Mybatis 是否支持延迟加载?如果支持,它的实现原理是什么?
Mybatis仅支持association关联对象和collection关联集合对象的延迟加载,association指的就是一对一,collection指的就是一对多查询。在Mybatis配置文件中,可以配置是否启用延迟加载lazyLoadingEnabled=true|false。
它的原理是,使用CGLIB创建目标对象的代理对象,当调用目标方法时,进入拦截器方法,比如调用a.getB().getName(),拦截器invoke()方法发现a.getB()是null值,那么就会单独发送事先保存好的查询关联B对象的sql,把B查询上来,然后调用a.setB(b),于是a的对象b属性就有值了,接着完成a.getB().getName()方法的调用。这就是延迟加载的基本原理。
当然了,不光是Mybatis,几乎所有的包括Hibernate,支持延迟加载的原理都是一样的。
77、Mybatis 的一级、二级缓存
一、mybatis一级缓存
1、一级缓存简介 一级缓存作用域是sqlsession级别的,同一个sqlsession中执行相同的sql查询(相同的sql和参数),第一次会去查询数据库并写到缓存中,第二次从一级缓存中取。
一级缓存是基于 PerpetualCache 的 HashMap 本地缓存,默认打开一级缓存。
2、何时清空一级缓存 如果中间sqlSession去执行commit操作(执行插入、更新、删除),则会清空SqlSession中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。
一级缓存时执行commit,close,增删改等操作,就会清空当前的一级缓存;当对SqlSession执行更新操作(update、delete、insert)后并执行commit时,不仅清空其自身的一级缓存(执行更新操作的效果),也清空二级缓存(执行commit()的效果)。
3、一级缓存无过期时间,只有生命周期 MyBatis在开启一个数据库会话时,会创建一个新的SqlSession对象,SqlSession对象中会有一个Executor对象,Executor对象中持有一个PerpetualCache对象,见下面代码。当会话结束时,SqlSession对象及其内部的Executor对象还有PerpetualCache对象也一并释放掉。
二、mybatis二级缓存
1、二级缓存简介 它指的是Mybatis中SqlSessionFactory对象的缓存。由同一个SqlSessionFactory对象创建的SqlSession共享其缓存。
二级缓存是 mapper 映射级别的缓存,多个 SqlSession 去操作同一个 Mapper 映射的 sql 语句,多个SqlSession 可以共用二级缓存,二级缓存是跨 SqlSession 的。
2、二级缓存何时存入 在关闭sqlsession后(close),才会把该sqlsession一级缓存中的数据添加到namespace的二级缓存中。
开启了二级缓存后,还需要将要缓存的pojo实现Serializable接口,为了将缓存数据取出执行反序列化操作,因为二级缓存数据存储介质多种多样,不一定只存在内存中,有可能存在硬盘中。
3、二级缓存有过期时间,但没有后台线程进行检测 需要注意的是,并不是key-value的过期时间,而是这个cache的过期时间,是flushInterval,意味着整个清空缓存cache,所以不需要后台线程去定时检测。
每当存取数据的时候,都有检测一下cache的生命时间,默认是1小时,如果这个cache存活了一个小时,那么将整个清空一下。
4、当 Mybatis 调用 Dao 层查询数据库时,先查询二级缓存,二级缓存中无对应数据,再去查询一级缓存,一级缓存中也没有,最后去数据库查找。
78、简述 Mybatis 的插件运行原理,以及如何编写一个插件
1)Mybatis 仅可以编写针对ParameterHandler、ResultSetHandler、StatementHandler、Executor 这 4种接口的插件,Mybatis 通过动态代理,为需要拦截的接口生成代理对象 以实现接口方法拦截功能,每当执行这 4种接口对象的方法时,就会进入拦截方法,具体就是InvocationHandler 的invoke()方法,当然,只会拦截那些你指定需要拦截的方法。
2)实现 Mybatis 的 Interceptor 接口并复写 intercept()方法,然后在给插件编写注解,指定要拦截哪一个接口的哪些方法即可,记住,别忘了在配置文件中配置你编写的插件。
79、Redis 的数据类型?
一、String(字符串)类型 String是最常用的一种数据类型,普通的key/ value 存储都可以归为此类 常用使用场景: 1、缓存结构体信息 2、计数功能
二、Hash Hash对象的键是一个字符串类型,值是一个键值对集合。 应用场景:该类型非常适合于存储对象的信息(结构体信息)。如一个用户有姓名,密码,年龄等信息。
三、List 可以向Redis列表的头部或尾部添加元素。 在插入时,如果该键并不存在,Redis 将为该键创建一个新的链表,如果这个键已经存在,则是向 list 添加元素。 应用场景: 1、list列表结构常用来做异步队列使用 2、list可用于秒杀抢购场景
四、set Redis 的 Set 是 String 类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据。Redis 中集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。 用在一些去重的场景里,例如每个用户只能参与一次活动、一个用户只能中奖一次等等去重场景 应用场景: 1、Redis set对外提供的功能与list类似是一个列表的功能,特殊之处在于set是可以自动排重的,当你需要存储一个列表数据,又不希望出现重复数据时,set是一个很好的选择, 并且set提供了判断某个成员是否在一个set集合内的重要接口,这个也是list所不能提供的。 2、Sets 集合的概念就是一堆不重复值的组合。利用Redis提供的Sets数据结构,可以存储一些集合性的数据,比如在微博应用中,可以将一个用户所有的关注人存在一个集合中,将其所有粉丝存在一个集合。 3、Redis还为集合提供了求交集、并集、差集等操作,可以非常方便的实现如共同关注、共同喜好、二度好友等功能,对上面的所有集合操作,你还可以使用不同的命令选择将结果返回给客户端还是存集到一个新的集合中。
五、zset (有序集合类型) Redis 有序集合和集合一样也是string类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个double类型的分数。 redis正是通过分数来为集合中的成员进行从小到大的排序。有序集合的成员是唯一的,但分数(score)却可以重复。集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。 应用场景: 1、Redis sorted set的使用场景与set类似,区别是set不是自动有序的,而sorted set可以通过用户额外提供一个优先级(score)的参数来为成员排序,并且是插入有序的,即自动排序。 当你需要一个有序的并且不重复的集合列表,那么可以选择sorted set数据结构,比如twitter 的public timeline可以以发表时间作为score来存储,这样获取时就是自动按时间排好序的。
2、另外还可以用Sorted Sets来做带权重的队列,比如普通消息的score为1,重要消息的score为2,然后工作线程可以选择按score的倒序来获取工作任务。让重要的任务优先执行。
80、Redis 相比 Memcached的区别和优势
Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载。它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提供动态、数据库驱动网站的速度,现在已被LiveJournal、hatena、Facebook、Vox、LiveJournal等公司所使用。
1、存储方式: memecache 把数据全部存在内存之中,断电后会挂掉,数据不能超过内存大小 redis有部份存在硬盘上,这样能保证数据的持久性,支持数据的持久化(笔者注:有快照和AOF日志两种持久化方式,在实际应用的时候,要特别注意配置文件快照参数,要不就很有可能服务器频繁满载做dump)。
2、数据支持类型: redis在数据支持上要比memecache多的多。
3、使用底层模型不同: 新版本的redis直接自己构建了VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求。
4、运行环境不同: redis目前官方只支持LINUX 上去行,从而省去了对于其它系统的支持,这样的话可以更好的把精力用于本系统 环境上的优化,虽然后来微软有一个小组为其写了补丁。但是没有放到主干上