JDBC
学习mybatis 之前,先学习jdbc
JDBC(Java Database Connectivity)是 Java 语言中用于连接和操作数据库的 API(应用程序编程接口)。它提供了一套标准的接口,使得 Java 程序能够与各种数据库进行交互,无论是关系型数据库(如 MySQL、PostgreSQL、Oracle 等)还是某些非关系型数据库。
接口规范:SUN(现为Oracle的一部分)提供了一套访问数据库的规范,即JDBC接口,这些接口定义了Java程序与数据库交互的标准方式。 驱动实现:各个数据库厂商会根据SUN的规范提供一套API,用于访问自己公司的数据库服务器。这些API实际上是JDBC接口的实现,被称为JDBC驱动。没有驱动,Java程序无法与数据库建立连接。 工作流程:当Java程序需要与数据库交互时,它会通过JDBC接口调用相应的方法。JDBC接口会将这些方法调用转发给对应的JDBC驱动,由驱动负责具体的数据库操作。 功能如下:
(1) 与数据库建连接。
(2) 向数据库发送 SQL 语句并执行这些语句。
(3) 处理数据返回的结果。
//加载驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//建立连接url(指定连接的路径 语法:“jdbc:mysql://ip地址:端口号/数据库名称”)
Connection con = DriverManager.getConnection("jdbc:mysql://10.16.158.90:3306/db1", "root", "123456");
// 创建statment
String updateSql = "SELECT * users SET password = ? WHERE id = ?";
PreparedStatement SelectStmt = conn.prepareStatement(updateSql);
//执行参数占用
updateStmt.setString(1, "new_password");
updateStmt.setInt(2, 1);
updateStmt.executeUpdate();
//执行sql,进行参数替换
ResultSet rs = SelectStmt.executeQuery();
//处理返回结果
List<Student> list= new LinkedList<>();
while (rs.next()){
Integer sid = rs.getInt("sid");
String name = rs.getString("name");
Integer age = rs.getInt("age");
Date birthday = rs.getDate("birthday");
Student stu = new Student(sid,name,age,birthday);
list.add(stu);
}
com.close();
stat.close();
com.close();
JDBC API主要位于JDK中的java.sql包中(之后扩展的内容位于javax.sql包中),主要包括:
-
Driver:驱动程序,会将自身加载到DriverManager中去,并处理相应的请求并返回相应的数据库连接(Connection)。
-
DriverManager:负责加载各种不同驱动程序(Driver),并根据不同的请求,向调用者返回相应的数据库连接(Connection)。
-
Connection:创建数据库连接,创建statement,创建PreparedStatement,以及事务相关提交回滚。
-
Statement:用以执行SQL查询和更新(针对静态SQL语句和单次执行)。
-
PreparedStatement:用以执行包含动态参数的SQL查询和更新(在服务器端编译,允许重复执行以提高效率)。
-
CallableStatement:用以调用数据库中的存储过程。
-
ResultSet:返回值
Mybatis:

SqlSession:作为MyBatis工作的主要顶层API,核心接口;通过 SqlSession 可以获取 Mapper,调用execute 执行;执行事务。 但是spring 业务代码里不在这里使用事务,这里只是mybatis用的事务。 Connection getConnection(); 获取链接,如果@transactional 已经开启事务,已经获取链接了。
Executor:MyBatis的内部执行器,它负责创建并调用StatementHandler,操作数据库,事务操作。
StatementHandler:调用ParameterHandler进行占位符替换,调用statmen执行sql,调用resultsethandler执行对象映射
prepare--创建statment; parameterize--->调用ParameterHandler;
ParameterHandler:负责将用户传递的参数转换成JDBC Statement所需要的参数。是MyBatis实现SQL入参设置的对象,参数绑定。执行setint,setString操作
ResultSetHandler:负责将JDBC返回的ResultSet结果集对象转换成List类型的集合。是MyBatis把ResultSet集合映射成POJO的接口对象。
TypeHandler:负责Java数据类型和JDBC数据类型之间的映射和转换。
transaciton:事务处理相关。
DataSource:Spring 默认的数据库连接池 HikariDataSource,采用配置,池化一管理connecttion。
Transaction(mybatis事务,含有dataSource)
AbstractRoutingDataSource可以实现动态数据源配置
SqlSource :负责根据用户传递的parameterObject,动态地生成SQL语句,将信息封装到BoundSql对象中,并返回。
BoundSql:动态 SQL 标签解析 → SQL 拼接 → #{} 替换为 ? → 参数映射构建
生成boundSql 过程
new BaseStatementHandler()
if (boundSql == null) { // issue #435, get the key before calculating the statement
generateKeys(parameterObject);
boundSql = mappedStatement.getBoundSql(parameterObject);
}
源码分析
Mybatis 扫描文件注册beanDefiniton过程(扫描的是接口):
@MapperScans->MapperScannerRegistrar->registerBeanDefinitions-->注册MapperScannerConfigurer.class[一顿设置属性]
MapperScannerConfigurer.class -》postProcessBeanDefinitionRegistry->mybatis自己的【ClassPathMapperScanner(重写isCandidateComponent方法)】->先调用父类的sacan扫描xmp文件,注册xmp为ScannedGenericBeanDefinition-->然后调用 org.mybatis.spring.mapper.ClassPathMapperScanner#processBeanDefinitions->然后把每一个bean中的class设置为MapperFactoryBean;此时每个mapper文件的bean定义类都变成MapperFactoryBean了,然后为每一个beanDefination 设置两个属性(addToConfig=true,sqlSessionFactory);
Mybatis 对上述的beanDefiniton进行实例化: getbean->doGetBean->instantce(MapperFactoryBean){this.mapperInterface = mapperInterface对接口名进行赋值} -->populateBean-->org.mybatis.spring.support.SqlSessionDaoSupport#setSqlSessionFactory-->(setSqlSessionFactory(SqlSessionFactory sqlSessionFactory))-->创建sqlSessionTemplate但是并不是创建sqlsession-->创建bean之后 -->-getObjectForBeanInstance->getObjectFromFactoryBean->doGetObjectFromFactoryBean->factory.getObject()->SqlSessionTemplate.getMapper【创建代理】--->getConfiguration> mapperRegistry.getMapper->mapperProxyFactory.newInstance->Proxy.newProxyInstance-->MapperProxy搞一个代理类;
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
mapperProxy:包含以下两个属性
private final SqlSession sqlSession;
private final Class<T> mapperInterface;
//解析出来的数据
SqlSession:
private final SqlSessionFactory sqlSessionFactory;
Mybaits 扫描xml文件过程
当创建第一个mapper Bean的时候->applyPropertyValues(有两个属性MapperFactoryBean,addToConfig)->发现SqlSessionFactory为空,创建SqlSessionFactory SqlSessionFactory——>SqlSessionFactoryBean.getObject()【bean.getObject】—> afterPropertiesSet--》buildSqlSessionFactory; xmlMapperBuilder.parse---》configurationElement解析sql----》放入到Configuration-放入到---》SqlSessionFactory交给bean容器进行管理。;
configuration 是重点包含以下属性:
Environment :(transactionFactory【创建事务】,DataSource)
protected final Map<String, MappedStatement> mappedStatements
Mybaits 执行流程:
@Autowired 每一个dao层的mapper都是一个mapperProxy
MapperProxy.invoke—》MapperMethodInvoker—-->PlainMethodInvoker-》MapperMethod.execute(convertArgsToSqlCommandParam获取参数) ——>sqlSession.update——>SqlSessionInterceptor.invoke—>getSqlSession(获取sqlsession)【如果开启事务,那sqlsession 已经打开了这个地方获取就行】--->sessionFactory.openSession(创建sqlsession)—>openSessionFromDataSource【创建事务,newExecutor】——>>new sqlsession--->DefaultSqlSession#selectList—>org.apache.ibatis.session.Configuration#getMappedStatement(java.lang.String)-->executor.query——>拦截Executor.query执行拦截器逻辑——>CachingExecutor-->先从缓存里查询数据--》doQuery(baseExecutor)【newStatementHandler[StatementHandler],prepareStatement【(getConnection)trasnction.getconnectuon获取jdbc链接,BaseStatementHandler#prepare(instantiateStatement实例化)【执行拦截器】——>parameterize[参数替换]——> PreparedStatementHandler.真正操作数据库(操作数据库)[StatementHandler];
四大对象拦截器
(1)写一个实现org.apache.ibatis.plugin.Interceptor接口的拦截器类,并实现其中的方法。 (2)添加@Intercepts注解,写上需要拦截的对象和方法,以及方法参数。 (3)Spring项目注意添加@Component注解即可,使其成为Spring管理的一个Bean。
mybatis 默认只拦截Executor、StatementHandler、ParameterHandler和ResultSetHandler。对于我们的自定义拦截器必须使用MyBatis提供的@Intercepts注解来指明我们要拦截的是四种类型中的哪一种接口。
@Intercepts({
@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class}),
@Signature(type = ParameterHandler.class, method = "setParameters", args = {PreparedStatement.class}),
@Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})
})
@Intercepts 标志该类是一个拦截器; @Signature 指明该拦截器具体需要拦截的哪个对象哪个方法;
type 四种类型接口中的某一个接口,如Executor.class。 method 对应接口中的某一个方法名,比如Executor的query方法。 args 对应接口中的某一个方法的参数,比如Executor中query方法因为重载原因,有多个,args就是指明参数类型,从而确定是具体哪一个方法。
插件用于封装目标对象的,通过该方法我们可以返回目标对象本身,也可以返回一个它的代理,可以决定是否要进行拦截进而决定要返回一个什么样的目标对象,官方提供了示例:return Plugin.wrap(target, this);,可以在这个方法中提前进行拦截对象类型判断,提高性能:
public Object plugin(Object target) {
//只对要拦截的对象生成代理
if(target instanceof StatementHandler){
//调用插件
return Plugin.wrap(target, this);
}
return target;
}
MyBatis拦截器用到责任链模式+动态代理+反射机制;
所有可能被拦截的处理类都会生成一个代理类,如果有N个拦截器,就会有N个代理,层层生成动态代理是比较耗性能的。而且虽然能指定插件拦截的位置,但这个是在执行方法时利用反射动态判断的,初始化的时候就是简单的把拦截器插入到了所有可以拦截的地方。所以尽量不要编写不必要的拦截器。
另外我们可以在调用插件的地方添加判断,只要是当前拦截器拦截的对象才进行调用,否则直接返回目标对象本身,这样可以减少反射判断的次数,提高性能。
执行拦截器的顺序: executor->ParameterHandler--》->StatementHandler-》->ResultSetHandler
- StatementHandler 拦截器拦截的方法不同,可能执行顺序不同。拦截prepare 执行顺序在ParameterHandler之前,拦截update 在ParameterHandler 之后
Mybatis 分页
1、RowBounds实现分页,mybatis提供了一个简单的逻辑分页使用类RowBounds,在DefaultSqlSession提供的 某些查询接口中我们可以看到RowBounds是作为参数用来进行分页的。 public List selectList(String statement, Object parameter, RowBounds rowBounds)
原理: 在DefaultResultSetHandler中,逻辑分页会将所有的结果都查询到,然后通过while循环来处理RowBounds的offset和limit值来获取最后的结果以达到分页的目的;所以在数据量大的sql中并不适用,它更适合在返回数据结果较少的查询中使用;
2、借助于sql语句进行分页,sql语句后面添加limit分页语句(page—helper)
PageHelper向Mybatis注册PageInterceptor分页拦截器(executor)[拦截的是execute.class]通过 PageHelper.startPage() 方法把分页相关的参数放到 ThreadLcoal 中 Mybatis 执行 SQL 过程中会调用拦截器根据查询 SQL 构建 count SQL从 ThreadLcoal 拿出分页信息,在查询 SQL 后面拼接 limit ?, ?,执行sql finially清空 ThreadLcoal。
Mybatis的优点:
(什么是mybatis) (1)Mybatis是一个半ORM(对象关系映射)框架,它内部封装了JDBC,不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。程序员直接编写原生态sql,可以严格控制sql执行性能,灵活度高。
(2)MyBatis 可以 XML 或注解来配置映射,将 java对象映射成数据库中的记录,避免了几乎所有的 JDBC 代码和手动设置参数。
解除sql与程序代码的耦合,便于统一管理; 支持动态参数; 很好的与各种数据库兼容; 能够与Spring很好的集成;
Mybatis的缺点:
(1)SQL语句的编写工作量较大,尤其当字段多、关联表多时,对开发人员编写SQL语句的功底有一定要求。 (2)SQL语句依赖于数据库,导致数据库移植性差,不能随意更换数据库。
什么是sql注入:
所谓SQL注入式攻击,就是攻击者把SQL命令插入到Web表单的输入域或页面请求的查询字符串,欺骗服务器执行恶意的SQL命令。
eg:
strSQL = "SELECT * FROM users WHERE (name = '" + userName + "') and (pw = '"+ passWord +"');"
//恶意写入
userName = "1' OR '1'='1";
passWord = "1' OR '1'='1";
//原来的sql变成,恒等成立
strSQL = "SELECT * FROM users WHERE (name = '1' OR '1'='1') and (pw = '1' OR '1'='1');"
常见的 (1)select * from user_table where username like '%#{username}%' 会报错,改为 select * from user_table where username like '%${username}%'sql注入 正确的写法: select * from user_table where username like concat('%',#{username},'%')
(2)传人ads,dsp;导致查不出数据 select * from user_table where username in (#{usernames}) 改为,会sql注入 select * from user_table where username in (${usernames}) 换成foreach select * from user_table where username in #{username}
(3)不能动态拼接,因为会换成‘’ select * from user_table order by #{column} limit 0,1 于是改成 select * from user_table order by ${column} limit 0,1
#{}和${}的区别是什么?
(1)${}是字符串替换,#{}是预处理;使用#{}可以有效的防止SQL注入,提高系统安全性。
(2)${}是参数直接注入,大括号内不能写jdbc类型,所以可能会引起sql的注入,#{}大括号可以写JDBc类型,防止sql注入提高了系统安全性。
(3)Mybatis在处理{}直接替换成变量的值。而Mybatis在处理#{}时,会对sql语句进行预处理,将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值;
什么是MyBatis的接口绑定?
MyBatis接口绑定指的是MyBatis允许开发者仅仅通过一个接口而不需要编写实现类,即可完成对数据库操作的映射。接口绑定有两种实现方式。
一种是通过注解绑定,就是在接口的方法上面加上 @Select、@Update等注解,里面包含Sql语句来绑定;
另外一种就是通过xml里面写SQL来绑定, 在这种情况下,要指定xml映射文件里面的namespace必须为接口的全路径名。
当Sql语句比较简单时候,用注解绑定, 当SQL语句比较复杂时候,用xml绑定,一般用xml绑定的比较多
Mybatis是如何将sql执行结果封装为目标对象并返回的?都有哪些映射形式?
1、当列名和封装查询结果的类的属性名一一对应时:这时MyBatis 有自动映射功能,将查询的记录封装到resultType 指定的类的对象中去
<mapper namespace="com.hanT.dao.UserDao">
<!--id对应接口中的方法名,resultType为返回结果的类型,要用类的全限定名-->
<select id="getUserList" resultType="com.hanT.pojo.User">
select * from mybatis.user
</select>
</mapper>
2、当列名和封装查询结果的类的属性名不对应时:
第一种是使用sql列的别名功能,将列的别名书写为对象属性名。[不常用]
<mapper namespace="com.hanT.dao.UserDao">
<!--id对应接口中的方法名,resultType为返回结果的类型,要用类的全限定名-->
<!--使用别名的功能,比如表里叫name,实体里叫tname-->
<select id="getUserList" resultType="com.hanT.pojo.User">
select name as tname from mybatis.user
</select>
</mapper>
第二种是使用标签,逐一定义数据库列名和对象属性名之间的映射关系。[最常用]
有了列名与属性名的映射关系后,Mybatis通过反射创建对象,同时使用反射给对象的属性逐一赋值并返回,那些找不到映射关系的属性,是无法完成赋值的。
Mapper 编写有几种方式
1、接口实现类继承 SqlSessionDaoSupport:使用此种方法需要编写mapper 接口,mapper 接口实现类、mapper.xml 文件
(1)在sqlMapConfig.xml中配置mapper.xml的位置:
<mappers>
<mapper resource="mapper.xml 文件的地址" />
<mapper resource="mapper.xml 文件的地址" />
</mappers>
(2)定义mapper接口:
(3)实现类集成SqlSessionDaoSupport:mapper方法中可以this.getSqlSession()进行数据增删改查。
(4)spring 配置:
<bean id="对象ID" class="mapper 接口的实现">
<property name="sqlSessionFactory" ref="sqlSessionFactory"></property>
</bean>
2、使用 MapperFactoryBean
(1)在sqlMapConfig.xml中配置mapper.xml的位置,如果mapper.xml和mappre接口的名称相同且在同一个目录,这里可以不用配置
<mappers>
<mapper resource="mapper.xml 文件的地址" />
<mapper resource="mapper.xml 文件的地址" />
</mappers>
(2)定义mapper接口:
① mapper.xml中的namespace为mapper接口的地址
② mapper接口中的方法名和mapper.xml中的定义的statement的id保持一致
③ Spring中定义:
<bean id="" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="mapper 接口地址" />
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
3、使用 mapper 扫描器
(1)mapper.xml文件编写:
mapper.xml中的namespace为mapper接口的地址;
mapper接口中的方法名和mapper.xml中的定义的statement的id保持一致;
如果将mapper.xml和mapper接口的名称保持一致则不用在sqlMapConfig.xml中进行配置。
(2)定义mapper接口:
注意mapper.xml的文件名和mapper的接口名称保持一致,且放在同一个目录
(3)配置mapper扫描器:
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="mapper接口包地址" />
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>
(4)使用扫描器后从spring容器中获取mapper的实现对象。
在mapper中如何传递多个参数?
(1)第一种:
//DAO层的函数
Public UserselectUser(String name,String area);
//对应的xml,#{0}代表接收的是dao层中的第一个参数,#{1}代表dao层中第二参数,更多参数一致往后加即可。
//这种方法不建议使用,sql层表达不直观,且一旦顺序调整容易出错。
<select id="selectUser"resultMap="BaseResultMap">
select * fromuser_user_t whereuser_name = #{0} anduser_area=#{1}
</select>
(2)第二种: 使用 @param 注解:
public interface usermapper {
user selectuser(@param(“username”) string username,@param(“hashedpassword”) string hashedpassword);
}
然后,就可以在xml像下面这样使用(推荐封装为一个map,作为单个参数传递给mapper):
<select id=”selectuser” resulttype=”user”>
select id, username, hashedpassword
from some_table
where username = #{username}
and hashedpassword = #{hashedpassword}
</select>
(3)第三种:多个参数封装成map
try{
//`#{}`里面的名称对应的是 `Map`里面的key名称。
//这种方法适合传递多个参数,且参数易变能灵活传递的情况。
public User selectUser(Map<String, Object> params);
<select id="selectUser" parameterType="java.util.Map" resultMap="UserResultMap">
select * from user
where user_name = #{userName} and dept_id = #{deptId}
</select>
(4)第四种:使用java bean进行传参
//`#{}`里面的名称对应的是 `User`类里面的成员属性。
//这种方法很直观,但需要建一个实体类,扩展不容易,需要加属性,看情况使用。
public User selectUser(User params);
<select id="selectUser" parameterType="com.test.User" resultMap="UserResultMap">
select * from user
where user_name = #{userName} and dept_id = #{deptId}
</select>
为什么说Mybatis是半自动ORM映射工具?它与全自动的区别在哪里?(关联对象,对象关系)
Hibernate属于全自动ORM映射工具,使用Hibernate查询关联对象或者关联集合对象时,可以根据对象关系模型直接获取,所以它是全自动的。而Mybatis在查询关联对象或关联集合对象时,需要手动编写sql来完成,所以,称之为半自动ORM映射工具
Mybatis的一级、二级缓存(在分布式场景中基本不用):
参考文章:tech.meituan.com/2018/01/19/…
一级缓存:
一、MyBatis一级缓存的生命周期和SqlSession一致。缓存内部设计简单,只是一个没有容量限定的HashMap,在缓存的功能性上有所欠缺
二、一级缓存的问题: MyBatis的一级缓存最大范围是SqlSession内部,有多个SqlSession或者分布式的环境下,数据库写操作会引起脏数据,建议设定缓存级别为Statement。
一级缓存是SqlSession级别的缓存,在创建SqlSession的时候会创建exectutor,其父类Base executor里有两个局部变量localCache,localOutputParameterCache即是缓存。在sqlsession 执行查询selectlist的时候会先调用baseExecutor的query。
然后根据将MappedStatement的Id、SQL的offset、SQL的limit、SQL本身以及SQL中的参数传入了CacheKey这个类,最终构成CacheKey
list = resultHandler == null ? (List) localCache.getObject(key) : null;
所以先从缓存里取,然后取到不到在查数据库
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
另外在query方法执行的最后,会判断一级缓存级别是否是STATEMENT级别,如果是的话,就清空缓存,这也就是STATEMENT级别的一级缓存无法共享localCache的原因。
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
// issue #482
clearLocalCache();
}
SqlSession的insert方法和delete方法,都会统一走update的流程
在执行update的时候,统一走一下
清理一下缓存。
二级缓存
一、MyBatis的二级缓存实现了SqlSession之间缓存数据的共享,同时粒度更加的细,能够到namespace级别,通过Cache接口实现类不同的组合,对Cache的可控性也更强。 二、由于默认的MyBatis Cache实现都是基于本地的,分布式环境下必然会出现读取到脏数据,需要使用集中式缓存将MyBatis的Cache接口实现,有一定的开发成本,直接使用Redis、Memcached等分布式缓存可能成本更低,安全性也更高。
二级缓存在一级缓存处理前,用CachingExecutor装饰了BaseExecutor的子类,在委托具体职责给delegate之前,实现了二级缓存的查询和写入功能。
缓存从 ms 里取的 ,所以它不是 sqlsession 级别的,而是namespace级别的
在CachingExecutor里有个TransactionalCacheManager
TransactionalCache实现了Cache接口,CachingExecutor会默认使用他包装初始生成的Cache,作用是如果事务提交,对缓存的操作才会生效,如果事务回滚或者不提交事务,则不对缓存产生影响。
Mybatis动态sql有什么用?执行原理?有哪些动态sql?
Mybatis动态sql可以在Xml映射文件内,以标签的形式编写动态sql,执行原理是根据表达式的值 完成逻辑判断并动态拼接sql的功能。 Mybatis提供了9种动态sql标签:trim | where | set | foreach | if | choose | when | otherwise | bind。
Xml映射文件中,除了常见的select|insert|updae|delete标签之外,还有哪些标签? 答:、、、、,加上动态sql的9个标签,其中为sql片段标签,通过标签引入sql片段,为不支持自增的主键生成策略标签
MyBatis如何获取自动生成的(主)键值?
insert 方法总是返回一个int值 ,这个值代表的是插入的行数 如果采用自增长策略,自动生成的键值在 insert 方法执行完后可以被设置到传入的参数对象中。 usergeneratedkeys="true" keyproperty="id
参考文章: blog.csdn.net/qq_39368007…
Mybatis 一对一,多对多配置:
select * from class c,teacher t where c.teacher_id=t.t_id and c.c_id=#{id}<resultMap type="com.lcb.user.Classes" id="ClassesResultMap">
<!-- 实体类的字段名和数据表的字段名映射 -->
<id property="id" column="c_id"/>
<result property="name" column="c_name"/>
<association property="teacher" javaType="com.lcb.user.Teacher">
<id property="id" column="t_id"/>
<result property="name" column="t_name"/>
</association>
</resultMap>
<!--collection 一对多关联查询 -->
<select id="getClass2" parameterType="int" resultMap="ClassesResultMap2">
select * from class c,teacher t,student s where c.teacher_id=t.t_id and c.c_id=s.class_id and c.c_id=#{id}
</select>
<resultMap type="com.lcb.user.Classes" id="ClassesResultMap2">
<id property="id" column="c_id"/>
<result property="name" column="c_name"/>
<association property="teacher" javaType="com.lcb.user.Teacher">
<id property="id" column="t_id"/>
<result property="name" column="t_name"/>
</association>
<collection property="student" ofType="com.lcb.user.Student">
<id property="id" column="s_id"/>
<result property="name" column="s_name"/>
</collection>
</resultMap>
MyBatis实现一对一有几种方式?具体怎么操作的?
有联合查询和嵌套查询 联合查询是几个表联合查询,只查询一次, 通过在resultMap里面配置association节点配置一对一的类就可以完成; 嵌套查询是先查一个表,根据这个表里面的结果的外键id,去再另外一个表里面查询数据,也是通过association配置,但另外一个表的查询通过select属性配置。
MyBatis实现一对多有几种方式,怎么操作的?
有联合查询和嵌套查询。联合查询是几个表联合查询,只查询一次,通过在resultMap里面的collection节点配置一对多的类就可以完成; 嵌套查询是先查一个表,根据这个表里面的 结果的外键id,去再另外一个表里面查询数据,也是通过配置collection,但另外一个表的查询通过select节点配置。
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()方法的调用。这就是延迟加载的基本原理。
Dao工作原理
通常一个Xml映射文件,都会写一个Dao接口与之对应,请问,这个Dao接口的工作原理是什么? 不错,整体执行流程了。 blog.csdn.net/a745233700/…
请问Dao接口里的方法,参数不同时,方法能重载吗? 不能被重载,key不能重复
不同XML id可以相同吗,为什么? 可以相同,因为namespace不同
参考文章: