技术标签: 三期面试题
mybatis是一个基于java的持久层
1、简单易学
mybatis本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置几个sql映射文件易于学习,易于使用,通过文档和源代码,可以比较完全的掌握它的设计思路和实现。
2、灵活
mybatis不会对应用程序或者数据库的现有设计强加任何影响。 sql写在xml里,便于统一管理和优化。通过sql基本上可以实现我们不使用数据访问框架可以实现的所有功能,或许更多。
3、解除sql与程序代码的耦合
通过提供DAL层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离,提高了可维护性。
4、提供映射标签,支持对象与数据库的orm字段关系映射
5、提供对象关系映射标签,支持对象关系组建维护
6、提供xml标签,支持编写动态sql。
1、编写SQL语句时工作量很大,尤其是字段多、关联表多时,更是如此。
2、SQL语句依赖于数据库,导致数据库移植性差,不能更换数据库。
3、框架还是比较简陋,功能尚有缺失,虽然简化了数据绑定代码,但是整个底层数据库查询实际还是要自己写的,工作量也比较大,而且不太容易适应快速数据库修改。
4、二级缓存机制不佳
mybatis的优点同样是mybatis的缺点,正因为mybatis使用简单,数据的可靠性、完整性的瓶颈便更多依赖于程序员对sql的使用水平上了。sql写在xml里,虽然方便了修改、优化和统一浏览,但可读性很低,调试也非常困难,也非常受限。
mybatis没有hibernate那么强大,但是mybatis最大的优点就是简单小巧易于上手,方便浏览修改sql语句。
publicclassEmployee {
privateInteger id;
privateString name;
privateString sex;
privateString memo;
privateDepartment department;
// getter/setter
}
<mappernamespace="com.mipo.mapping.EmployeeMapper">
<select id="queryAllEmployees" resultMap="employeeResultMap">
<![CDATA[
SELECT UID, DEPT_ID, EMP_NAME,EMP_SEX, EMP_MEMO FROM T_EMPLOYEE
]]>
</select>
<resultMapid="employeeResultMap" type="com.mipo.domain.Employee">
<id column="uid" property="id" />
<result column="emp_name" property="name"/>
<result column="emp_sex" property="sex"/>
<result column="emp_memo" property="memo"/>
<association
property="department"
javaType="com.mipo.domain.Department"
select="com.mipo.mapping.DepartmentMapper.selectById"
column="dept_id"
fetchType="eager"/>
</resultMap>
</mapper>
部门<mappernamespace="com.mipo.mapping.DepartmentMapper">
<resultMapid="departmentResultMap" type="com.mipo.domain.Department">
<id column="UID" property="id" />
<result column="DEP_NAME" property="name" />
</resultMap>
<select id="selectById" resultMap="departmentResultMap">
<![CDATA[
SELECT UID, DEP_NAME FROMT_DEPARTMENT WHERE UID = #{value}
]]>
</select>
</mapper>
pojo
publicclassDepartment {
privateInteger id;
privateString name;
privateList<Employee> employees;
// getter/setter
}
一对多的xml配置<mappernamespace="com.mipo.mapping.DepartmentMapper">
<select id="queryAllDepartments" resultMap="departmentResultMap">
<![CDATA[
SELECT UID, DEP_NAME FROMT_DEPARTMENT
]]>
</select>
<resultMapid="departmentResultMap" type="com.mipo.domain.Department">
<id column="UID" property="id" />
<result column="DEP_NAME" property="name" />
<collection property="employees"
ofType="com.mipo.domain.Employee"
select="com.mipo.mapping.EmployeeMapper.selectById"
column="uid"
fetchType="lazy"/>
</resultMap>
</mapper>
<mappernamespace="com.mipo.mapping.EmployeeMapper“>
<resultMapid="employeeResultMap" type="com.mipo.domain.Employee">
<id column="uid" property="id" />
<result column="emp_name" property="name"/>
<result column="emp_sex" property="sex"/>
<result column="emp_memo" property="memo"/>
</resultMap>
<select id="selectById" resultMap="employeeResultMap">
<![CDATA[
SELECT UID,
EMP_NAME,
EMP_SEX,
EMP_MEMO
FROM T_EMPLOYEE
WHERE DEPT_ID = #{value}
]]>
</select>
</mapper>
<!--调用存储过程 -->
<select id="callProc" statementType="CALLABLE" parameterType="java.util.Map">
<![CDATA[
{CALL PROC_GET_USER_NAME(
#{uid, mode = IN, jdbcType = INTEGER},
#{name, mode = OUT, jdbcType = VARCHAR}
)}
]]>
</select>
publicclass UserDaoImpl implements UserDao {
@Override
publicString callProc(Map<String, Object> data) {
SqlSession sqlSession = getSqlSessionFactory().openSession(true);
try{
returnsqlSession.selectOne("com.mipo.mapping.UserMapper.callProc", data);
} finally {
sqlSession.close();
}
}
private SqlSessionFactory getSqlSessionFactory() {
returnSqlSessionFactoryHelper.getSessionFactory();
}
}
publicclass UserDaoTest {
private static UserDao userDao = null;
@BeforeClass
publicstatic void setUpBeforeClass() throws Exception {
userDao = new UserDaoImpl();
}
@Test
publicvoid testCallProc() {
Map<String, Object>data = new HashMap<String, Object>();
data.put("uid", 1);
data.put("name", null);
userDao.callProc(data);
System.out.println("name = " + data.get("name"));
}
}
<!-- 根据帐号、密码查询纪录-->
<select id="selectUser" parameterType="java.util.HashMap" resultType="user">
SELECT T.UID,
T.NAME,
T.PWD
FROM T_USER T
WHERE 1 = 1
<choose>
<when test="name != null">
AND T.NAME like concat('%', #{name}, '%')
</when>
<when test="pwd != null">
AND T.PWD = #{ pwd}
</when>
<otherwise>
AND T.UID = 1
</otherwise>
</choose>
</select>
publicclass UserDaoImpl implements UserDao {
@Override
publicList<User> queryUser(Map<String, Object> data) {
SqlSession sqlSession = getSqlSessionFactory().openSession(true);
try{
returnsqlSession.selectList("com.mipo.mapping.UserMapper.selectUser",data);
} finally {
sqlSession.close();
}
}
private SqlSessionFactory getSqlSessionFactory() {
returnSqlSessionFactoryHelper.getSessionFactory();
}
}
publicclass UserDaoTest {
private static UserDao userDao = null;
@BeforeClass
publicstatic void setUpBeforeClass() throws Exception {
userDao = new UserDaoImpl();
}
@Test
publicvoid testSelectUser() {
Map<String, Object>data = new HashMap<String, Object>();
data.put("name", "oracle");
for(User user : userDao.queryUser(data)) {
System.out.println(user.getName() + " :: " + user.getPwd());
}
}
}
答:${}是Properties文件中的变量占位符,它可以用于标签属性值和sql内部,属于静态文本替换,比如${driver}会被静态替换为com.mysql.jdbc.Driver。#{}是sql的参数占位符,Mybatis会将sql中的#{}替换为?号,在sql执行前会使用PreparedStatement的参数设置方法,按序给sql的?号占位符设置参数值,比如ps.setInt(0, parameterValue),#{item.name}的取值方式为使用反射从参数对象中获取item对象的name属性值,相当于param.getItem().getName()。
答:还有很多其他的标签,<resultMap>、<parameterMap>、<sql>、<include>、<selectKey>,加上动态sql的9个标签,trim|where|set|foreach|if|choose|when|otherwise|bind等,其中<sql>为sql片段标签,通过<include>标签引入sql片段,<selectKey>为不支持自增的主键生成策略标签。
注:这道题也是京东面试官面试我时问的。
答:Dao接口,就是人们常说的Mapper接口,接口的全限名,就是映射文件中的namespace的值,接口的方法名,就是映射文件中MappedStatement的id值,接口方法内的参数,就是传递给sql的参数。Mapper接口是没有实现类的,当调用接口方法时,接口全限名+方法名拼接字符串作为key值,可唯一定位一个MappedStatement,举例:com.mybatis3.mappers.StudentDao.findStudentById,可以唯一找到namespace为com.mybatis3.mappers.StudentDao下面id = findStudentById的MappedStatement。在Mybatis中,每一个<select>、<insert>、<update>、<delete>标签,都会被解析为一个MappedStatement对象。
Dao接口里的方法,是不能重载的,因为是全限名+方法名的保存和寻找策略。
Dao接口的工作原理是JDK动态代理,Mybatis运行时会使用JDK动态代理为Dao接口生成代理proxy对象,代理对象proxy会拦截接口方法,转而执行MappedStatement所代表的sql,然后将sql执行结果返回。
答:Mybatis使用RowBounds对象进行分页,它是针对ResultSet结果集执行的内存分页,而非物理分页,可以在sql内直接书写带有物理分页的参数来完成物理分页功能,也可以使用分页插件来完成物理分页。
分页插件的基本原理是使用Mybatis提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的sql,然后重写sql,根据dialect方言,添加对应的物理分页语句和物理分页参数。
举例:select * from student,拦截sql后重写为:select t.* from (select * from student)t limit 0,10
答:Mybatis仅可以编写针对ParameterHandler、ResultSetHandler、StatementHandler、Executor这4种接口的插件,Mybatis使用JDK的动态代理,为需要拦截的接口生成代理对象以实现接口方法拦截功能,每当执行这4种接口对象的方法时,就会进入拦截方法,具体就是InvocationHandler的invoke()方法,当然,只会拦截那些你指定需要拦截的方法。
实现Mybatis的Interceptor接口并复写intercept()方法,然后在给插件编写注解,指定要拦截哪一个接口的哪些方法即可,记住,别忘了在配置文件中配置你编写的插件。
答:能,JDBC都能,Mybatis当然也能。
答:Mybatis动态sql可以让我们在Xml映射文件内,以标签的形式编写动态sql,完成逻辑判断和动态拼接sql的功能,Mybatis提供了9种动态sql标签trim|where|set|foreach|if|choose|when|otherwise|bind。
其执行原理为,使用OGNL从sql参数对象中计算表达式的值,根据表达式的值动态拼接sql,以此来完成动态sql的功能。
答:第一种是使用<resultMap>标签,逐一定义列名和对象属性名之间的映射关系。第二种是使用sql列的别名功能,将列别名书写为对象属性名,比如T_NAME AS NAME,对象属性名一般是name,小写,但是列名不区分大小写,Mybatis会忽略列名大小写,智能找到与之对应对象属性名,你甚至可以写成T_NAME AS NaMe,Mybatis一样可以正常工作。
有了列名与属性名的映射关系后,Mybatis通过反射创建对象,同时使用反射给对象的属性逐一赋值并返回,那些找不到映射关系的属性,是无法完成赋值的。
答:能,Mybatis不仅可以执行一对一、一对多的关联查询,还可以执行多对一,多对多的关联查询,多对一查询,其实就是一对一查询,只需要把selectOne()修改为selectList()即可;多对多查询,其实就是一对多查询,只需要把selectOne()修改为selectList()即可。
关联对象查询,有两种实现方式,一种是单独发送一个sql去查询关联对象,赋给主对象,然后返回主对象。另一种是使用嵌套查询,嵌套查询的含义为使用join查询,一部分列是A对象的属性值,另外一部分列是关联对象B的属性值,好处是只发一个sql查询,就可以把主对象和其关联对象查出来。
那么问题来了,join查询出来100条记录,如何确定主对象是5个,而不是100个?其去重复的原理是<resultMap>标签内的<id>子标签,指定了唯一确定一条记录的id列,Mybatis根据<id>列值来完成100条记录的去重复功能,<id>可以有多个,代表了联合主键的语意。
同样主对象的关联对象,也是根据这个原理去重复的,尽管一般情况下,只有主对象会有重复记录,关联对象一般不会重复。
举例:下面join查询出来6条记录,一、二列是Teacher对象列,第三列为Student对象列,Mybatis去重复处理后,结果为1个老师6个学生,而不是6个老师6个学生。
t_id t_name s_id
| 1 | teacher | 38 |
| 1 | teacher | 39 |
| 1 | teacher | 40 |
| 1 | teacher | 41 |
| 1 | teacher | 42 |
| 1 | teacher | 43 |
答: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,支持延迟加载的原理都是一样的。
答:不同的Xml映射文件,如果配置了namespace,那么id可以重复;如果没有配置namespace,那么id不能重复;毕竟namespace不是必须的,只是最佳实践而已。
原因就是namespace+id是作为Map<String, MappedStatement>的key使用的,如果没有namespace,就剩下id,那么,id重复会导致数据互相覆盖。有了namespace,自然id就可以重复,namespace不同,namespace+id自然也就不同。
答:使用BatchExecutor完成批处理。
答:Mybatis有三种基本的Executor执行器,SimpleExecutor、ReuseExecutor、BatchExecutor。
SimpleExecutor:每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象。
ReuseExecutor:执行update或select,以sql作为key查找Statement对象,存在就使用,不存在就创建,用完后,不关闭Statement对象,而是放置于Map<String, Statement>内,供下一次使用。简言之,就是重复使用Statement对象。
BatchExecutor:执行update(没有select,JDBC批处理不支持select),将所有sql都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个Statement对象,每个Statement对象都是addBatch()完毕后,等待逐一执行executeBatch()批处理。与JDBC批处理相同。
作用范围:Executor的这些特点,都严格限制在SqlSession生命周期范围内。
答:在Mybatis配置文件中,可以指定默认的ExecutorType执行器类型,也可以手动给DefaultSqlSessionFactory的创建SqlSession的方法传递ExecutorType类型参数。
答:Mybatis可以映射枚举类,不单可以映射枚举类,Mybatis可以映射任何对象到表的一列上。映射方式为自定义一个TypeHandler,实现TypeHandler的setParameter()和getResult()接口方法。TypeHandler有两个作用,一是完成从javaType至jdbcType的转换,二是完成jdbcType至javaType的转换,体现为setParameter()和getResult()两个方法,分别代表设置sql问号占位符参数和获取列查询结果。
答:虽然Mybatis解析Xml映射文件是按照顺序解析的,但是,被引用的B标签依然可以定义在任何地方,Mybatis都可以正确识别。
原理是,Mybatis解析A标签,发现A标签引用了B标签,但是B标签尚未解析到,尚不存在,此时,Mybatis会将A标签标记为未解析状态,然后继续解析余下的标签,包含B标签,待所有标签解析完毕,Mybatis会重新解析那些被标记为未解析的标签,此时再解析A标签时,B标签已经存在,A标签也就可以正常解析完成了。
答:Mybatis将所有Xml配置信息都封装到All-In-One重量级对象Configuration内部。在Xml映射文件中,<parameterMap>标签会被解析为ParameterMap对象,其每个子元素会被解析为ParameterMapping对象。<resultMap>标签会被解析为ResultMap对象,其每个子元素会被解析为ResultMapping对象。每一个<select>、<insert>、<update>、<delete>标签均会被解析为MappedStatement对象,标签内的sql会被解析为BoundSql对象。
答:Hibernate属于全自动ORM映射工具,使用Hibernate查询关联对象或者关联集合对象时,可以根据对象关系模型直接获取,所以它是全自动的。而Mybatis在查询关联对象或关联集合对象时,需要手动编写sql来完成,所以,称之为半自动ORM映射工具。
面试题看似都很简单,但是想要能正确回答上来,必定是研究过源码且深入的人,而不是仅会使用的人或者用的很熟的人,以上所有面试题及其答案所涉及的内容,在我的Mybatis系列博客中都有详细讲解和原理分析。
setSelected:animated:方法实现了,可以满足在当前cell里处理cell的点击事件。有好些无需数据交互的事件放在这里处理看上去很简洁,妙哉。但是!当滚动tableview,cell被重用的时候,会导致setSelected:animated:多次调用。如何不在解决呢?1、老老实实的实现 tableView: didSelectRowAtIndexPath: 方法。这...
题目描述每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0...m-1报数....这样下去.....
Microsoft Visual C++ Debug LibraryDebug Assertion Failed!Program:E:\gyb.2.5\hotfoxd.exeFile:dbgdel.cppLine:52Expression:_BLOCK_TYPE_IS_VALID(pHead->nBlockUse)对话框如下图所示: 该对话框为调式环境下断言失败提示
产品描述/产品详情:该软件包可为支持的型号和操作系统提供 HP PC Hardware Diagnostics UEFI。HP PC Hardware Diagnostics UEFI 可为支持的型号提供基于 UEFI 的硬件诊断,用于验证系统是否可以正常运行。此外,还提供了其它支持,以更新和管理系统 BIOS 和系统上的其它设备固件。----------------当前更新-----------...
os模块的popen方法当需要得到外部程序的输出结果时,本方法非常有用,返回一个类文件对象,调用该对象的read()或readlines()方法可以读取输出内容。os.popen(cmd) 要得到命令的输出内容,只需再调用下read()或readlines()等 如a=os.popen(cmd).read()文件test.pyimport sysx = sys.stdinfor line in x: print("receive",line)文件main.pyimport osa
我们都知道建立索引能够提高查询效率,那么是不是任何情况下都能提高呢,当然不是的的,下面我们就来列举一些常见的索引失效的场景。借用上一篇文章的dm_person_info表在card_code列没加索引的时,查询时间如下,大概都在0.07秒。我们来加上索引试试,加上后查询效率高了许多。在正确使用索引的情况下,查询一行数据的时间不到10毫秒,所以显示0.00 sec .1.列类...
随笔 - 45 文章 - 1 评论 - 58 【Python百知百问】1.Python浅复制和深复制——copy和deepcopy方法问题:Python里面如何拷贝一个对象? 参考:《Python Cookbook》 参考链接:http://blog.csdn.net/sharkw/article/detail
2012年8月21号开始了我的第一篇博文,也开始了我的研究生生涯。怀着对机器学习和计算机视觉等等领域的懵懂,从一个电子材料的领域跨入这个高速发展的人工智能领域。从开始的因无知而惊慌,因陌生而乏力,到一步步的成长。这过程的知识积累也都大部分反映在这个博客上面了。感谢这个平台促使自己去总结去坚持去进步。也感谢这个平台给我带来了和大家交流的机会。借此博文总结自己过去与未来可能散乱的博文。在此也谢谢大家一
Maven 的常用命令 1 2 3 4 5 1、mvn compile 编译,将Java 源程序编译成class字节码文件。 2、mvn test 测试,并生成测试报告 3、mvn clean 将以前编译得到的旧的class字节码文件删除 4、mvn pakage 打包,动态 web工程打 war包,J...
setTimeout(代码,延迟时间);参数说明:1. 要调用的函数或要执行的代码串。2. 延时时间:在执行代码前需等待的时间,以毫秒为单位(1s=1000ms)。当我们打开网页3秒后,在弹出一个提示框,代码如下:setTimeout("alert('Hello!')", 3000 );当按钮start被点击时,setTimeout()调用函数,在5秒后弹出一个提示框。function tinfo...
使用CubeCamera创建反光效果1.demo效果2. 实现要点2.1 创建立方体相机CubeCamera2.2 使用动态环境贴图材质2.3 render中更新立方体相机2.4 创建场景的全景贴图2.5 创建场景中的模型3. demo代码1.demo效果2. 实现要点2.1 创建立方体相机CubeCamera创建立方体的语法如下const cubeCamera = new THREE.CubeCamera( near, far, cubeRenderTarget)参数说明near
摘要: 步进电机的细分控制是由驱动器精确控制步进电机的相电流来实现的,以二相电机为例,假如电机的额定相电流为3A,如果使用常规驱动器(如常用的恒流斩波方式)驱动该电机,电机每运行一步,其绕组内的电流将从0突变为3A ... 步进电机的运行性能与它的步进驱动器有密切的联系,可以通过驱动技术的改进来克服步进电机的缺点。相对于其他的驱动方式,细分驱动方式不仅可以减小步进电机的步距角