DAO接口如何设计?

增加:
增加一条记录,批量增加若干条记录
这个要传递的是整个的对象


删除:
删除一条记录,批量删除若干条,删除全部(应该也可以认为是批量删除的一种)
我只要传递数据库中的那个没有意义的主键就可以了


修改:
修改一条记录,批量修改若干条记录
传递的应该也是对象,不过批量修改是不是比较好实现呢?


查询:


查询全部
模糊查询(按我的想法,模糊查询似乎是比较好实现的)

 

单个条件查询
分别按数据库中各个有意义的字段查询(条件是“等于”)
如果我想修改一条记录的时候,我需要先根据表的那个主键得到一个记录显示在网页上,然后再根据这个主键去更新记录。
结合上一步,就出现了2个按编号查询,只不过一个可能是有意义的(比如员工编号),另一个是没有多大意义的编号,这时候,我应该怎么设置Dao里面的方法呢?似乎都应该是queryById呢

 

组合查询
组合查询的条件很多,比如我想查A、B两个部门的年龄大于50的人,应该怎么做呢?这个条件写SQL语句当然很简单,但是我的接口应该怎么设计呢?
或者我想查A员工某个月的工资,又应该怎么查,怎么传递参数呢?

查询似乎是最复杂的了,接口怎么设计?DAO里面如何实现?

 

-----------------------------------------------------------------

其实这个担心是多余滴,因为面向接口编程不是这个意思。貌似是说每个DAO层的类都有自己的接口,而不是说所有类共用一个接口。当初跟谁学的这么干啊,哎~~

 

<think>我们正在讨论MyBatis中XML映射文件如何映射到DAO接口的原理。根据之前的引用,特别是引用[1][2][3][4],我们可以总结出以下关键点:1.**Mapper接口与XML映射文件的对应关系**:-Mapper接口的全限定名(包括包名和接口名)必须与XML映射文件中`<mapper>`标签的`namespace`属性值完全一致。-Mapper接口中的方法名必须与XML映射文件中定义的SQL语句的`id`属性值相同。-方法的输入参数类型和输出参数类型也需要与XML中定义的`parameterType`和`resultType`(或`resultMap`)相匹配。2.**动态代理机制**:-MyBatis在运行时通过动态代理技术为Mapper接口生成代理对象(ProxyObject)。-当调用Mapper接口的方法时,代理对象会拦截方法调用,并根据接口的全限定名和方法名,拼接成一个字符串(如`com.example.MyMapper.selectById`),然后根据这个字符串去查找对应的MappedStatement(即映射的SQL语句)。3.**MappedStatement的作用**:-每一个在XML映射文件中定义的`<select>`,`<insert>`,`<update>`,`<delete>`标签都会被解析为一个MappedStatement对象,其中包含了SQL语句、参数映射、结果映射等信息。4.**绑定过程**:-在MyBatis初始化时,会解析XML映射文件,将解析得到的MappedStatement注册到全局配置对象(Configuration)中,以`namespace.id`作为key。-同时,当解析到一个XML映射文件时,会检查`namespace`属性是否对应一个存在的接口,如果存在,则为该接口生成动态代理类,并将该接口和代理工厂注册到MapperRegistry中。5.**方法调用流程**:-当我们通过SqlSession的`getMapper`方法获取Mapper接口的实例时,实际上是从MapperRegistry中获取对应的代理工厂,然后创建代理对象。-当我们调用代理对象的方法时,代理对象会调用`MapperProxy`的`invoke`方法,该方法会根据接口类型和方法信息,获取对应的MappedStatement,并执行相应的数据库操作。现在,我们将根据以上原理详细解释MyBatis的底层实现机制,并按照要求进行回答。###MyBatisXML映射文件到DAO接口的映射原理MyBatis的核心设计思想是通过XML配置或注解将Java方法与SQL语句进行解耦,实现SQL和Java代码的分离。其中,XML映射文件与DAO接口的映射机制建立在**动态代理**和**方法签名解析**的基础上。####🔧1.**映射文件解析过程**在MyBatis启动时,配置文件(如`mybatis-config.xml`)中的每个`<mapper>`配置都会被加载:-如果配置的是XML映射文件路径(如`<mapperresource="com/example/UserMapper.xml"/>`),MyBatis会解析该XML文件。-解析时,会将`<mappernamespace="...">`中的`namespace`属性作为接口的全限定名进行验证(是否与某个Java接口对应)。-随后解析XML中的SQL标签(如`<selectid="methodName">`),每个SQL标签被解析为一个`MappedStatement`对象,该对象包含以下关键信息:-`id`:SQL语句的唯一标识,格式为`${namespace}.${id}`(如`com.example.UserMapper.selectById`)。-`sqlSource`:实际的SQL语句(可能包含动态SQL标签)。-`parameterType`:参数类型。-`resultType`或`resultMap`:结果映射。这些`MappedStatement`对象会被存储在全局`Configuration`对象的`mappedStatements`属性中(一个Map结构)[^4]。####🧬2.**接口与XML绑定机制**-**接口合法性验证**:在解析XML过程中,MyBatis会检查`namespace`属性值是否指向一个存在的Java接口。如果存在,则建立映射关系。-**动态代理对象生成**:通过`SqlSession.getMapper(Class<T>type)`获取接口实例时:1.MyBatis从`MapperRegistry`中获取该接口对应的`MapperProxyFactory`(代理工厂)[^1]。2.调用`newInstance(MapperProxy<T>)`生成代理对象:```javapublicTnewInstance(SqlSessionsqlSession){finalMapperProxy<T>mapperProxy=newMapperProxy<>(sqlSession,mapperInterface,methodCache);return(T)Proxy.newProxyInstance(mapperInterface.getClassLoader(),newClass[]{mapperInterface},mapperProxy);}```3.生成的代理对象实现了目标接口,并将所有方法调用委托给`MapperProxy`[^1]。####🔌3.**方法调用拦截与路由**当调用代理对象的方法时:1.动态代理拦截器`MapperProxy.invoke()`会执行以下操作:-如果方法是`Object`类中的方法(如`toString`,`equals`),直接调用。-否则,将方法签名(接口名+方法名)作为key,查找对应的`MappedStatement`[^2]。2.根据方法参数类型和XML配置,动态执行以下步骤:-将Java参数转换为SQL参数(通过`TypeHandler`)。-执行SQL(调用`Executor.query()`或`Executor.update()`)。-将结果集转换为返回类型(如`List<User>`或`User`)。####📊4.**映射关系匹配示例假设有以下DAO接口:```javapackagecom.example;publicinterfaceUserMapper{UserselectById(intid);}```对应的XML映射文件(UserMapper.xml):```xml<mappernamespace="com.example.UserMapper"><selectid="selectById"parameterType="int"resultType="com.example.User">SELECT*FROMuserWHEREid=#{id}</select></mapper>```当调用`userMapper.selectById(1)`时:1.代理对象解析方法签名为:`com.example.UserMapper.selectById`。2.在`Configuration.mappedStatements`中查找key为`com.example.UserMapper.selectById`的`MappedStatement`。3.执行SQL语句:`SELECT*FROMuserWHEREid=?`,参数`?`替换为1。####🌐5.**动态代理技术的应用**MyBatis通过JDK动态代理机制实现DAO接口的映射:-**依赖接口而非类**:代理对象基于接口实现,因此要求DAO必须是接口(无实现类),这也是为什么MyBatis的DAO通常定义为接口[^1]。-**方法签名一致性**:方法名称、参数类型、返回值类型必须与XML配置严格匹配,否则会因找不到`MappedStatement`而报错[^3]。####⚙️6.**注解与XML的共存机制**如果同时使用XML和注解配置同一方法,MyBatis的处理逻辑是:-优先使用XML配置的`MappedStatement`(覆盖注解)。-注解配置通过`@Select`,`@Insert`等声明SQL语句,同样会转化为`MappedStatement`存储在全局配置中。####🚀性能优化策略-**`MapperRegistry`缓存**:每个接口的代理工厂只创建一次,提升多次调用性能。-**方法级缓存**:`MapperMethod`对象(包含SQL执行逻辑)被缓存在`methodCache`中,避免重复解析方法签名[^1]。####§相关问题1.如果XML中的`namespace`写错,MyBatis会如何处理?2.如何在MyBatis中同时使用XML映射和注解配置?3.MyBatis如何支持重载的DAO方法?4.为什么Dao接口没有实现类,MyBatis却能执行方法?5.如何动态替换MyBatis映射文件中的SQL?---以上原理深刻体现了MyBatis通过**接口抽象**和**动态代理**实现了SQL操作的优雅封装,同时提供了灵活的扩展机制。####参考资料[^1]:MyBatis动态代理机制源码分析:`org.apache.ibatis.binding.MapperProxy`[^2]:XML映射与DAO接口绑定原理:`org.apache.ibatis.session.Configuration#addMapper`[^3]:MappedStatement注册过程:`org.apache.ibatis.builder.xml.XMLMapperBuilder#parse`[^4]:MyBatis官方文档关于XML配置说明:[mybatis.org](https://mybatis.org/mybatis-3/configuration.html)</think> ### MyBatis XML映射到DAO接口的实现原理 MyBatis通过**动态代理**和**方法签名映射**实现XML文件到DAO接口的无缝连接,以下是核心实现机制: #### 🔍 一、核心映射机制(基于方法签名) 1. **命名空间绑定** XML中的`<mapper namespace="com.example.UserDao">`必须**完全匹配**DAO接口的全限定名: ```xml <!-- XML映射文件 --> <mapper namespace="com.example.UserDao"> <select id="findById" parameterType="int" resultType="User"> SELECT * FROM users WHERE id = #{id} </select> </mapper> ``` 2. **方法签名映射** 接口方法与XML语句的映射关系: ```java // DAO接口声明 public interface UserDao { User findById(int id); // 方法名匹配XML的id } ``` - **映射键值**:`namespace.id` → `com.example.UserDao.findById` - **参数匹配**:方法参数类型对应XML的`parameterType` - **返回类型**:方法返回类型匹配XML的`resultType/resultMap` #### 🧬 二、动态代理实现流程 1. **初始化阶段(加载映射)** MyBatis启动时解析XML映射文件: ```mermaid graph TB A[XML文件解析] --> B[创建MappedStatement对象] B --> C{存储到<br>Configuration.mappedStatements} C -->|Key = namespace+id| D["Map<String, MappedStatement>"] ``` 2. **接口代理生成** ```java // 通过SqlSession获取DAO代理 UserDao userDao = sqlSession.getMapper(UserDao.class); // 底层实现: public class DefaultSqlSession { public <T> T getMapper(Class<T> type) { return configuration.getMapper(type, this); } } ``` - **代理工厂**:`MapperProxyFactory`创建JDK动态代理对象 - **代理处理器**:`MapperProxy`实现`InvocationHandler`接口 3. **方法调用拦截** ```java // MapperProxy核心逻辑 public Object invoke(Object proxy, Method method, Object[] args) { // 拼接方法全路径:com.example.UserDao.findById String commandKey = method.getDeclaringClass().getName() + "." + method.getName(); // 获取MappedStatement MappedStatement ms = configuration.getMappedStatement(commandKey); // 执行SQL(委托给Executor) return executor.query(ms, args); } ``` #### ⚙️ 三、关键数据结构 1. **MappedStatement 成员** | 属性 | 说明 | XML对应标签 | |------|------|-------------| | `id` | 唯一标识 | `<select>/<update>`等 | | `sqlSource` | SQL语句 | SQL文本 | | `parameterType` | 参数类型 | `parameterType` | | `resultType` | 返回类型 | `resultType` | 2. **Configuration 注册中心** ```java public class Configuration { // 存储所有映射关系 protected final Map<String, MappedStatement> mappedStatements = new StrictMap<>(); // 注册Mapper接口 public <T> void addMapper(Class<T> type) { // 验证接口名=namespace // 动态生成代理类 } } ``` #### 💡 四、设计优势解析 1. **解耦实现** - **接口无实现类**:开发者只需声明接口方法 - **SQL集中管理**:XML分离业务代码与SQL逻辑 2. **动态扩展能力** 通过`@Select`等注解可实现XML替代方案: ```java @Select("SELECT * FROM users WHERE id = #{id}") User findById(int id); ``` #### ⚠️ 五、常见问题处理 1. **映射失败场景** | 问题类型 | 原因 | |----------|------| | `BindingException` | namespace不匹配或方法名错误 | | `ParameterType`错误 | 接口参数与SQL参数不兼容 | | `ResultType`冲突 | 返回对象字段与SQL列不匹配 | 2. **接口方法重载限制** MyBatis**不支持重载方法**(如`findById(int)`和`findById(String)`),因为: $$ \text{映射键} = \text{namespace} + \text{方法名} $$ 方法名重复会导致映射键冲突 #### 🧪 六、验证实验 可在MyBatis日志开启DEBUG模式观察: ```properties # log4j.properties log4j.logger.org.apache.ibatis=DEBUG ``` 执行时输出: ``` DEBUG - Creating proxy for mapper interface com.example.UserDao DEBUG - Mapped statement: com.example.UserDao.findById ``` ####
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值