点击上方 Java后端,选择 设为星标
优质文章,及时送达
昨天刚刚面完 Spring,根据hr的反馈说面试官对我的整体表现还算满意,然后又通知我今天有空去再聊聊有关的技术。去的路上,我一直在想,今天会问些什么问题,JVM?多线程?还是分布式......真是越想心里越没底。想着想着就到了,尽管还是那个熟悉的面试官,但那张年轻有为的面孔丝毫没有让我放下紧张的情绪。他先开口了:昨天的面试感觉你挺好的,你说你项目中还用的是mybatis框架作为数据库访问,那我们今天就来聊聊吧。
MyBatis是支持定制化SQL、存储过程以及高级映射的优秀的持久层框架。
它避免了几乎所有JDBC代码和手动设置参数以及获取结果集。
MyBatis可以对配置和原生Map使用简单的XML或注解,将接口和Java的POJO映射成数据库中的记录。
mybatis的着力点在于POJO和SQL之间的映射关系,然后通过映射配置文件,将SQL所需的参数,以及返回的结果字段映射到指定POJO。
Hibernate的ORM实现了POJO和数据库表之间的映射,以及SQl的自动生成和执行,也就是说Hibernate会根据制定的存储逻辑,自动生成对应的SQl并调用JDBC接口加以执行。
下面我通过四个方面对比两者的区别:
调优方案:
Hibernate与具体数据库的关联只需在XML文件中配置即可,所有的HQL语句与具体使用的数据库无关,移植性很好。
mybatis默认情况下没开启缓存;
要开启二级缓存,需要在sql映射文件中加上;
映射文件中的所有select语句将会缓存,映射文件中的所有insert/update/delete会刷新缓存;
缓存会使用LRU(最近最少使用)算法来回收;
缓存会存储列表集合会对象的1024个引用;
缓存会被视为read/write(可读可写)缓存,意味着对象检索不是共享的,而且可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。
Hibernate的一级缓存是Session缓存,利用好一级缓存就需要对Session的生命周期进行管理好;
二级缓存是SessionFactory级的缓存,分为内置缓存和外置缓存。
另外,有种说法,mybatis是半自动ORM映射工具,Hibernate是全自动的。
这主要就是因为使用Hibernate查询关联对象或集合对象时,可以根据对象关系模型调用api接口直接获取。
而Mybatis在查询关联对象或集合对象时,需要手动编写sql来完成,所以叫做半自动。
至于我们公司为什么选择半自动的mybatis,主要是因为我们的业务经常需要编写复杂的sql,比如动态的sql。
还有这种更便于我们使用索引来优化sql语句。
(1)加载JDBC驱动(2)建立并获取数据库连接(3)创建JDBC Statements对象(4)设置SQL语句的传入参数(5)执行SQL语句并获得查询结果(6)对查询结果进行转换处理并将处理结果返回(7)释放相关资源(关闭Connection,关闭Statement,关闭ResultSet)
好的。
可以这么说,Myabtis初始化的过程就是创建Configuration对象的过程。
过程也很简单:
(1)加载配置文件mybatis-config.xml到Mybatis内部。
(2)使用Configuration对象作为一个所有配置信息的容器,这个对象的组织结构和XML配置文件的组织结构几乎完全一样,这样配置文件的信息就可以存到这个对象中,访问起来很方便。
没看过。
。
关键的类还是知道一点的。
(心想:
既然面试前准备了,还是要说的,不然怎么显得自己nb一些)
作为mybatis工作的主要顶层API,表示和数据库交互的会话,完成必要的数据库增删改查功能。
mybatis执行器,是Mybatis调度的核心,负责SQL语句的生成和查询缓存的维护。
封装了JDBCStatement操作,负责对JDBC Statement的操作。
负责对用户传递的参数转换成JDBC Statement所需要的参数。
负责将JDBC返回的ResultSet结果集转换成List类型的集合。
(6)TypeHandler:
负责Java数据类型和jdbc数据类型之间的映射和转换。
维护了一条select/update/delete/insert节点的封装。
负责根据用户传递的parameterObject,动态生成SQL语句,将信息封装在BoundSql对象中,并返回。
表示动态生成的SQL语句以及相应的参数信息。
Mybatis所有的配置信息都维护在这个对象中。
我先说下xml的配置方式吧。
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>x.x.x</version>
</dependency>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!-- 当mybatis的xml文件和mapper接口不在相同包下时,需要用mapperLocations属性指定xml文件的路径。
*是个通配符,代表所有的文件,**代表所有目录下 -->
<property name="mapperLocations" value="classpath:mapper/**/*.xml"/>
<!-- 加载mybatis的全局配置文件 -->
<property name="configLocation" value="classpath:mybatis/mybatis-config.xml" />
</bean>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mybatis="http://mybatis.org/schema/mybatis-spring"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring.xsd">
<!-- 自动扫描 -->
<mybatis:scan base-package="org.mybatis.spring.sample.mapper" />
</beans>
(接着说)现在好像大多使用的是注解配置mybatis和数据源的方式,也就是使用java代码和spring提供的注解。
(其实步骤大致差不多,由于涉及安全问题代码不透露,想学习的可以网上找。
)
随手写了一个,接着解释到:
这个语句被称作selectPerson,接收一个int类型的参数,并返回一个HashMap类型的对象,其中的键是列名,值便是结果行中的对应值。
<select id="selectPerson" parameterType = "int" resultType="hashmap"
select * from person where id =#{id}
</select>
select中有这些属性可选:
必选的,命名空间中唯一的标识符,可以被用来引用这条语句。
可选,将会传入这条语句的参数类的完全限定名或别名。
从这条语句返回的期望类型的类的完全限定名或别名。
外部resultMap的命名引用。
注意使用resultType或resultMap,不能同时使用。
默认false。
设置为true表示只要语句被调用,都会导致本地缓存和二级缓存被清空。
对select元素为true。
设置为true会导致本条语句的结果被二级缓存。
默认值为unset(依赖驱动)。
这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。
默认值为unset(依赖驱动)。
这是尝试影响驱动程序每次批量返回的结果行数和这个设置值相等。
默认值PREPARED。
这会让mybatis分别使用Statement,PreparedStatement或CallableStatement。
有两种方法。
通过在sql语句中定义别名,强行让返回的字段名的的别名和实体类中的属性名一致。
<select id="getByOrderId" parameterType="java.lang.Long" resultType="com.demo.entity.OrderInfo">
select order_id OrderId, order_sn orderSn, total_fee totalFee, create_time createTime
from order_info where order_id=#{orderId}
</select>
<resultMap id = "BaseResultMap" type="com.demo.entity.OrderInfo">
<id property="OrderId" column="order_id"/>
<result property="orderSn" column="order_sn"/>
<result property="totalFee" column="total_fee"/>
<result property="createTime" column="create_time"/>
</resultMap>
<select id="getByOrderId" parameterType="java.lang.Long" resultMap="BaseResultMap">
select order_id, order_sn, total_fee, create_time
from order_info where order_id=#{orderId}
</select>
一般我们插入数据的话,如果想要知道刚刚插入的数据的主键是多少,可以通过以下方式来获取。
<insert id='insert' parameterType="com.demo.entity.OrderInfo"
<selectKey keyProperty="orderId" order="AFTER" resultType="java.lang.Long">
select LAST_INSERT_ID()
</selectKey>
insert into order_info(order_sn,total_fee,create_time)
values(#{orderSn},#{totalFee},#{createTime)
</insert>
做条件判断的,如果不使用这个标签,肯定要在代码中做判断,比如元素是否为空,字符串是否是空字符串,还比如一些特定的枚举值需要判断执行条件。
这个标签组合类似于if/else if.../else,就是多个选项中选择一个,如果都不满足条件,那只能执行中的内容了。
<select id="getStudentListChoose" parameterType="Student" resultMap="BaseResultMap">
SELECT * from STUDENT WHERE 1=1
<where>
<choose>
<when test="Name!=null and student!='' ">
AND name LIKE CONCAT(CONCAT('%', #{student}),'%')
</when>
<when test="hobby!= null and hobby!= '' ">
AND hobby = #{hobby}
</when>
<otherwise>
AND AGE = 15
</otherwise>
</choose>
</where>
</select>
each标签:
用于循环。
例如:
<select id="listByOrderIds" resultMap="BaseResultMap">
select * from order_info where order_id in
<foreach collection="list" item="item" open="(" close=")" separator=",">
#{item}
</foreach>
</select>
#{}是解析传进来的参数,而另一个是拼接参数到SQl中。
#{}是预编译处理,而另一个是字符串替换。
而且#{}可以防止SQL注入。
select * from emp where name=#{empName},参数传入empName->Smith,解析执行后的SQL是:
select * from emp where name=?。
但是对于select * from emp where name=${empName},参数传入empName->Smith,解析执行后的SQL是:
select * from emp where name='Smith'。
有两种方法:
//mapper接口
public OrderInfo getByOrderIdAndStatus(Long orderId, String status);
//mapper.xml文件
<select id="getByOrderIdAndStatus" resultMap="BaseResultMap">
select * from order_info where order_id=#{0} and status=#{1}
</select>
//mapper接口
public OrderInfo getByOrderIdAndStatus(@param("orderId")Long orderId, @param("status")String status);
//mapper.xml文件
<select id="getByOrderIdAndStatus" resultMap="BaseResultMap">
select * from order_info where order_id=#{orderId} and status=#{status}
</select>
2.使用Map集合作为参数来装载
Map<String, Object> map = new HashMap();
map.put("orderId", 1L);
map.put("status", "NORMAL");
OrderInfo orderinfo = getByOrderIdAndStatus(map);
//mapper接口
public OrderInfo getByOrderIdAndStatus(Map<String, Object> map);
//mapper.xml文件
<select id="getByOrderIdAndStatus" parameterType="map" resultMap="BaseResultMap">
select * from order_info where order_id=#{orderId} and status=#{status}
</select>
面试官:不错,看来你对mybatis运用的挺熟练的了。今天的面试先到这里了,回家等消息吧。
我:好的。。
-END-
如果看到这里,说明你喜欢这篇文章,请转发、点赞
。同时标星(置顶)本公众号可以第一时间<今天你面试了吗>系列博文推送。
推荐阅读
1. 《今天你面试了吗?》Spring篇
2. ArrayList 的使用及原理,面试必问的知识点
3. 2020 年 4 月全国程序员工资出炉!
4. 面试官:MySQL千万级别大表,你要如何优化?
本文分享自微信公众号 - Java后端(web_resource)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。