在学习日记二中,我们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接口文件。