在学习日记二中,我们springbot+mybatis实现是通过mapper接口,mapper.xml。每一个的mapper.xml都对应一个mapper接口,我们的service调用的时候也是调用对应的mapper接口然后去操作数据库。所以当数据库的表多的情况或者sql语句复杂的情况下,为了项目的维护方便。以及不必需要那么多的mapper接口。
所以我们可以通过传统的SqlSessionTemplate来实现一个通用的Dao对mapper.xml直接操作。直接不需要mapper接口层。不通过mapper接口层来执行对应sql。直接使用通用的Dao来调用。
既然不需要mapper接口了。所以我们把SpringBootDemoApplication项目启动类扫描mapper接口的@MapperScan注释掉,如果没有再启动类里面写@MapperScan而是单独写@Mapper,请把@Mapper注释掉。
package com.lds.springbootdemo; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.PropertySource; @SpringBootApplication /*直接在Mapper类上面添加注解@Mapper,这种方式要求每一个mapper类都需要添加此注解,麻烦 所以可以通过使用@MapperScan可以指定要扫描的Mapper类的包的路径 */ //注释掉 //@MapperScan("com.lds.springbootdemo.mapper") // 用来指定配置文件的位置 @PropertySource(value={"classpath:dbconfig.properties"}) public class SpringBootDemoApplication { public static void main(String[] args) { SpringApplication.run(SpringBootDemoApplication.class, args); } }
此时我们就可以删除mapper接口了。删不删都行,反正现在注释掉了@MapperScan或者@Mapper。
注意此处一个坑!!!
亲身实践,说多了都是泪啊(⊙﹏⊙)。注释掉了@MapperScan或者@Mapper之后,如果你没有删除mapper接口的java文件。如果原来在****mapper.java接口文件写的有@Select、@Update,@Insert、@dalete等注解,一定也要注释掉。不然就会报如下的错,会根据项目来变,所以我就把报错信息的变量替换成了:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name '***': Unsatisfied dependency expressed through field '***';
nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name '***': Unsatisfied dependency expressed through field '***';
nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name '***': Unsatisfied dependency expressed through field 'sqlSessionTemplate';
nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'sqlSessionTemplate' defined in class path resource [org/mybatis/spring/boot/autoconfigure/MybatisAutoConfiguration.class]: Unsatisfied dependency expressed through method 'sqlSessionTemplate' parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sqlSessionFactory' defined in class path resource [org/mybatis/spring/boot/autoconfigure/MybatisAutoConfiguration.class]: Bean instantiation via factory method failed;
nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.apache.ibatis.session.SqlSessionFactory]: Factory method 'sqlSessionFactory' threw exception; nested exception is org.springframework.core.NestedIOException: Failed to parse mapping resource: 'file ***'; nested exception is java.lang.IllegalArgumentException: Mapped Statements collection already contains value for com.lds.repastsystem.mapper.employeeMapper.selectByAccount. please check file [***] and ***Mapper.java (best guess)
解决办法:
- 删除mapper.java接口
- 注释掉****mapper.java接口文件写的有@Select、@Update,@Insert、@dalete等注解
然后我们application.properties项目配置文件中的mybatis的mapper映射路径必须要写
#设置项目访问名称 server.servlet.context-path=/springBootDemo #mysql数据源配置 spring.datasource.driver-class-name=${driverClass} spring.datasource.url=${url} spring.datasource.username=${user} spring.datasource.password=${password} #mybatis的mapper映射路径 mybatis.mapperLocations=classpath:mybatis/mapper/*.xml
之后的操作就是进行编写Dao。
在com.lds.repastsystem包下新建一个dao包。然后编写Dao的接口的实现类。
Dao.java
package com.lds.springbootdemo.dao; /** * @program: springbootdemo * @description: * @author: * @createData: * @updateAuthor: * @updateData: * @updateContent: * @Version: 1.0 * @email: lidongshenglife@163.com * @blog: www.b0c0.com */ public interface Dao { /** * 保存对象 * @param str * @param obj * @return * @throws Exception */ public Object save(String str, Object obj) throws Exception; /** * 修改对象 * @param str * @param obj * @return * @throws Exception */ public Object update(String str, Object obj) throws Exception; /** * 删除对象 * @param str * @param obj * @return * @throws Exception */ public Object delete(String str, Object obj) throws Exception; /** * 查找返回一个对象 * @param str * @param obj * @return * @throws Exception */ public Object selectForOne(String str, Object obj) throws Exception; /** * 查找返回多个对象 * @param str * @param obj * @return * @throws Exception */ public Object selectForList(String str, Object obj) throws Exception; }
DaoSupport.java
package com.lds.springbootdemo.dao; import org.apache.ibatis.session.ExecutorType; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; import java.util.List; /** * @program: springbootdemo * @description: * @author: * @createData: * @updateAuthor: * @updateData: * @updateContent: * @Version: 1.0 * @email: lidongshenglife@163.com * @blog: www.b0c0.com */ /* 注解了@Repository的类上如果数据库操作中抛出了异常,就能对其进行处理, 转而抛出的是翻译后的spring专属数据库异常,方便我们对异常进行排查处理)。 */ @Repository /* 加入 @Transactional 注解,使用默认配置,抛出异常之后,事务会自动回滚,数据不会插入到数据库。 */ @Transactional public class DaoSupport implements Dao { /** * @Autowired默认按类型装配(这个注解是属于spring的), * 默认情况下必须要求依赖对象必须存在,如果要允许null值, * 可以设置它的required属性为false,如:@Autowired(required=false) , * 如果我们想使用名称装配可以结合@Qualifier注解进行使用 @Autowired()@Qualifier("baseDao") * @Resource(这个注解属于J2EE的),默认按照名称进行装配,名称可以通过name属性进行指定, * 如果没有指定name属性,当注解写在字段上时,默认取字段名进行安装名称查找, * 如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。 * 但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。 */ @Autowired private SqlSessionTemplate sqlSessionTemplate; /** * 保存对象 * @param str * @param obj * @return * @throws Exception */ public Object save(String str, Object obj) throws Exception { return sqlSessionTemplate.insert(str, obj); } /** * 批量更新 * @param str * @param objs * @return * @throws Exception */ public Object batchSave(String str, List objs)throws Exception{ return sqlSessionTemplate.insert(str, objs); } /** * 修改对象 * @param str * @param obj * @return * @throws Exception */ public Object update(String str, Object obj) throws Exception { return sqlSessionTemplate.update(str, obj); } /** * 批量更新 * @param str * @param objs * @return * @throws Exception */ public void batchUpdate(String str, List objs)throws Exception{ SqlSessionFactory sqlSessionFactory = sqlSessionTemplate.getSqlSessionFactory(); //批量执行器 SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH,false); try{ if(objs!=null){ for(int i=0,size=objs.size();i<size;i++){ sqlSession.update(str, objs.get(i)); } sqlSession.flushStatements(); sqlSession.commit(); sqlSession.clearCache(); } }finally{ sqlSession.close(); } } /** * 删除对象 * @param str * @param obj * @return * @throws Exception */ public Object delete(String str, Object obj) throws Exception { return sqlSessionTemplate.delete(str, obj); } /** * 批量删除 * @param str * @param objs * @return * @throws Exception */ public Object batchDelete(String str, List objs )throws Exception{ return sqlSessionTemplate.delete(str, objs); } /** * 查找返回一个对象 * @param str * @param obj * @return * @throws Exception */ public Object selectForOne(String str, Object obj){ return sqlSessionTemplate.selectOne(str, obj); } /** * 查找返回多个对象 * @param str * @param obj * @return * @throws Exception */ public Object selectForList(String str, Object obj){ return sqlSessionTemplate.selectList(str, obj); } }
里面有两个参数,第一个参数就直接填希望去执行mapper.xml中的sql。第二个就是我们需要sql中需要传递的参数。就好比登录验证实例来说:
sbd_userMapper.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.lds.springbootdemo.mapper.sbd_userMapper"> *****省略部分内容***** <select id="selectByAccount" parameterType="java.lang.String" resultMap="BaseResultMap"> select <include refid="Base_Column_List" /> from sbd_user where Account = #{account,jdbcType=VARCHAR} </select> *****省略部分内容***** </mapper>
mapper标签的namespace属性的值:com.lds.springbootdemo.mapper.sbd_userMapper
执行的sqlId的值:selectByAccount
我们的第一个参数就是:
com.lds.springbootdemo.mapper.sbd_userMapper.selectByAccount
注意此处一个坑!!!
亲身实践,说多了都是泪啊(⊙﹏⊙)。第一个参数一定要和mapper标签的namespace属性的值相对应,不能直接写成sbd_userMapper.selectByAccount!!!因为我就犯了这样的错误。导致报了这样一个错
org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.PersistenceException:
### Error querying database. Cause: java.lang.IllegalArgumentException: Mapped Statements collection does not contain value for ***
### Cause: java.lang.IllegalArgumentException: Mapped Statements collection does not contain value for ***
*** 在我这个项目里指的是sbd_userMapper.selectByAccount,
之后让我找很长时间的原因,最后一步一步debug进入源码调试看到才发现必须要和namespace属性的值一致。不能省略前面的内容。
解决办法:
- 调用的时候参数写完整com.lds.springbootdemo.mapper.sbd_userMapper.selectByAccount
- 如果不想写com.lds.springbootdemo.mapper前面的这一串的话,可以直接把mapper.xml的mapper标签的namespace属性的值改成namespace=”sbd_userMapper”即可,调用的时候参数就直接写成sbd_userMapper.selectByAccount即可。
然后就是lognService去通过调用通用Dao。
LoginServiceImpl.java
package com.lds.springbootdemo.service.login_register; import com.lds.springbootdemo.dao.DaoSupport; import com.lds.springbootdemo.domain.sbd_user; import com.lds.springbootdemo.mapper.sbd_userMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * @program: springbootdemo * @description: * @author: * @createData: * @updateAuthor: * @updateData: * @updateContent: * @Version: 1.0 * @email: lidongshenglife@163.com * @blog: www.b0c0.com */ @Service public class LoginServiceImpl implements LoginService { //@Autowired //private sbd_userMapper userMapper; @Autowired private DaoSupport dao; /** * 登录验证Service * @return */ @Override public boolean loginValidate(String account,String password) { boolean retu=false; //sbd_user em=userMapper.selectByAccount(account); sbd_user user= null; try { user = (sbd_user) dao.selectForOne("sbd_userMapper.selectByAccount",account); } catch (Exception e) { e.printStackTrace(); } if(user!=null&&password.equals(user.getPassword())){ retu=true; } return retu; } }
然后就可以实现了,这样的话不管以后有多少模块功能,只要和数据库的操作直接调用这个通用Dao就行,不必再写很多的Mapper.java接口文件。