在mybatis中,命名空间除了隔离sql外,还具有定义mapper接口地址的特殊功能。
问题:
java面向接口编程语言,不使用接口编程。数据库的操作应定义一些操作接口,如用户添加、用户删除、用户查询等。,并调用dao接口完成数据库操作。
上面的代码应该调用selectone、selectList,人工判断完全不方便。selectlist和selectone方法参数是object类型。如果程序员在编码中设置参数错误,则在编译阶段不会报告错误。假设:mybatis封装能否包装 方法实现体,方法名:和mapper.statementid在xml中保持一致。输入参数类型的方法:和mapper.parametertype在xml中的指定类型相同。方法返回值:可根据dao接口进行返回 返回值的类型决定是调用selectone还是selectlist,类型和mapper.resulttype类型在xml中保持不变 一致使用mybatis提供动态代理来生成接口来实现对象。mapper.xml知道mapper.java,通过namespace配置对应关系。
改为mapper 接口实现:第一步:定义mapper.xml
Mapper.用原来的xml文件不变。
第二步:定义mapper 接口
///mapper接口路径和mapper.namespace在xml中一致publice interface UserMapper { //根据mapper.statement定义在xml中 //方法名:和mapper.statementid在xml中的id一致 //输入参数:和mapper.parametertype在xml中一致 //输入结果:和mapper.resultttype在xml中一致 public User findUserById(int id)throws Exception; ////自定义条件查询用户信息 public List<User> findUserList(User user)throws Exception; ///查询用户列表输出mapp public List<Map> findUserListReturnMap(User user)throws Exception; ///使用resultmap查询用户列表 public List<User> findUserListResultMap(User user)throws Exception; ///插入用户 public void insertUser(User user)throws Exception; /////查询用户列表总数 public int findUserCount(User user)throws Exception; ////查询用户传mapp public List<User> findUserListByMap(Map map)throws Exception;}
界面定义具有以下特点:
1、Mapper接口方法名和mapper.定义在xml中的每个sqlid都是相同的
2、输入参数类型和Mapper接口方法.定义在xml中的每个sql parametertype类型相同
3、输出参数类型和Mapper接口方法.在xml中定义的每个sqlresulttype类型相同
第三步:修改namespaceMapper.xml映射文件中的namepace改为以下内容:
<mapper namespace="cn.itcast.mybatis.mapper.UserMapper">
namespace修改后是maper接口的地址。
第四步:通过maper接口调用statementent:
public class UserMapperTest { SqlSessionFactory sqlSessionFactory; @Before public void setUp() throws Exception { // 创建会话工厂 // SqlsessionFactory创建的会话工厂 String resource = "SqlMapConfig.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); // SqlsessionFactory创建的会话工厂 sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } @Test public void testFindUserById() throws Exception { SqlSession sqlSession = sqlSessionFactory.openSession(); //////生成mapper接口的代理对象 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); //调用 mapper接口的方法 User user = userMapper.findUserById(1); System.out.println(user); } @Test public void testInsertUser() throws Exception { SqlSession sqlSession = sqlSessionFactory.openSession(); //////生成mapper接口的代理对象 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); //构造查询条件 User user_insert = new User(); user_insert.setUsername(张三四); user_insert.setSex("1"); //调用 mapper接口的方法 userMapper.insertUser(user_insert); ///提交事务 sqlSession.commit(); sqlSession.close(); } }
session.getMapper(UserMapper.class)生成代理对象作为UserMaper的接口来实现对象。
总结:Mapper接口动态代理对象生成规则:1、mapper接口路径和mapper.namespace在xml中一致2、方法名:和mapper.statementid在xml中的id一致3、输入参数:和mapper.parametertype在xml中一致4、输入结果:和mapper.xml中的resulttype一致两个文件:mapper.xml(mapper映射文件)和mapper.java(mapper接口文件)
2.SqlMapConfig.xml配置内容SqlMapConfig.xml中配置的内容和顺序如下:
properties(属性)
settings(全局配置参数)
typeAliases(类型名称)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境集合属性对象)
(环境子属性对象)
(事务管理)
(数据源)
mappers(映射器)
properties(属性)SqlMapConfig.java属性文件中的配置信息如下:
dbaspath在classpath下定义.properties文件,
jdbc.driver=com.mysql.jdbc.Driverjdbc.url=jdbc:mysql://localhost:3306/mybatisjdbc.username=rootjdbc.password=mysql
引用如下:
<properties resource="db.properties" /> <environments default="development"> <environment id="development"> <transactionManager type="JDBC" /> <dataSource type="POOLED"> <property name="driver" value="${jdbc.driver}" /> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> </dataSource> </environment> </environments>
mappersMapper配置的几种方法:<mapper resource=" " />使用相对于类路径的资源,如:<mapper resource="sqlmap/user.xml" /><mapper url=" " />使用完全有限的路径如下:<mapper url="file:///D:\workspace_spingmvc\mybatis_01\config\sqlmap\user.xml" /><mapper class=" " />使用mapper接口类路径如下:<mapper class="cn.itcast.mybatis.mapper.UserMapper"/>注:该方法要求mapper接口名称与mapper映射文件名称相同,并将其放在同一目录中。<package name=""/>注册指定包下的所有mapper接口如下:<package name="cn.itcast.mybatis.mapper"/>注:该方法要求mapper接口名称与mapper映射文件名称相同,并放置在同一目录中。
3.Mapper.xml操作数据库的sql定义在映射文件中,每个sql都是statement,映射文件是mybatis的核心。
parametertype输入类型
#{}与${}#{}实现的是将参数值设置在preparestatement中的预处理语句中,sql语句中#{}表示占位符。
<!-- 根据id查询用户信息 --><select id="selectUserById" parameterType="int" resultType="user">select * from user where id = #{id}</select>
使用占位符#{}可以有效防止sql注入,使用时不需要注意参数值的类型。mybatis会根据参数值的类型调用不同的statement来设置参数值。可以想象,如果参数值是字符串,则自动映射生成的sql中的参数值两侧自动有单引号,如果参数值是数字型,则自动映射生成的sql中的参数值两侧没有单引号。
注:当传输单个值时,#{}中的参数名称通常与maper接口的形状参数名称相同,也可设置为任意值。
${}不同于#{},${}是将参数值不加修饰的拼接在sql中,相当于用jdbc的statement拼接sql,使用${}不能防止sql注入,但有时使用${}会非常方便,如下:
<!-- 用户信息根据名称模糊查询 --><select id="selectUserByName" parameterType="string" resultType="user"> select * from user where username like '%${value}%'</select>
如果在这个例子中使用#{},输入的字符串中必须有%,而%是人工拼接在参数中,这显然有点麻烦。如果使用${}在sql中拼接为%,则调用mapper接口传输参数要方便得多。
//如果使用占位符号,则必须人为添加%
List<User> list = userMapper.selectUserByName(%管理员%);
//如果使用${}原始符号,则不需要人为添加参数的%
List<User> list= userMapper.selectUserByName(管理员);
再比如order by排序,如果列名通过参数传输到sql,则根据传输的列名进行排序,则应写为:
ORDER BY ${columnName}
如果使用#{},此功能将无法实现。
注意:${}不能防止sql注入,对系统安全影响很大。如果使用${},建议用户尽量不要自动填写输入参数,即使用户需要填写,也要检查填写的数据,以确保安全。
另外,当传递单个值时,${}中填写的参数名称经测试填写value是不错的。
传输pojo对象Mybatis使用ognl表达式来分析对象字段的值,例如:
<!—pojo对象综合查询用户信息 --> <select id="selectUserByUser" parameterType="user" resultType="user"> select * from user where id=#{id} and username like '%${username}%' </select>
user对象中的字段名称标注在上面的大括号上。
测试:
public void testselectUserByUser()throws Exception{ //获得session SqlSession session = sqlSessionFactory.openSession(); //受限mapper接口实例 UserMapper userMapper = session.getMapper(UserMapper.class); ///构造查询条件user对象 User user = new User(); user.setId(1); user.setUsername(管理员); /////传输user对象查询用户列表 List<User> list = userMapper.selectUserByUser(user); //关闭sesssion session.close(); }
异常测试:
在Sql中字段名输入错误后进行测试,username输入dusername测试结果报错:
org.apache.ibatis.exceptions.PersistenceException: ### Error querying database. Cause: org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 'dusername' in 'class cn.itcast.mybatis.po.User'### Cause: org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 'dusername' in 'class cn.itcast.mybatis.po.User'
传输hashmap
Sql映射文件定义如下:
<!-- hashmap综合查询用户信息
<selectid="selectUserByHashmap"parameterType="hashmap"resultType="user">
select * from user where id=#{id} and username like '%${username}%'
</select>
hashmapkey标有上面的红色。
测试:
public void testselectUserByHashmap()throws Exception{ //获得session SqlSession session = sqlSessionFactory.openSession(); //受限mapper接口实例 UserMapper userMapper = session.getMapper(UserMapper.class); ///Hashmap对象的构造查询条件 HashMap<String, Object> map = new HashMap<String, Object>(); map.put("id", 1); map.put("username", “管理员”; ////传输Hashmap对象查询用户列表 List<User> list = userMapper.selectUserByHashmap(map); //关闭sesssion session.close(); }
异常测试:
map中的key与sql中分析的key不一致。
测试结果没有报错,只是通过key获得值为空。
resultType(输出类型)输出简单类型参考getnow输出日期类型,看下面的例子输出整形:
文件
<!-- 获取用户列表总数 --><select id="selectUserCount" parameterType="user" resultType="int"> select count(1) from user</select>
Mapper接口
public int selectUserCount(User user) throws Exception;
调用:
public void testselectUserCount() throws Exception{ //获得session SqlSession session = sqlSessionFactory.openSession(); ///获得mapper接口实例 UserMapper userMapper = session.getMapper(UserMapper.class); User user = new User(); user.setUsername(管理员); ////传输Hashmap对象查询用户列表 int count = userMapper.selectUserCount(user); ///使用session实现 //int count = session.selectOne("cn.itcast.mybatis.mapper.UserMapper.selectUserCount", user); //关闭sesssion session.close(); }
总结:
必须查询输出简单类型的结果集有一个记录,最后将第一个字段的值转换为输出类型。
使用session的selectone可以查询单个记录。
Sql片段可在Sql中提取重复的Sql,使用时可引用include,最终达到Sql重用的目的,如下:
<!-- pojo综合查询用户信息 --> <select id="selectUserByUser" parameterType="user" resultType="user"> select * from user <where> <if test="id!=null and id!=null and id!=''"> and id=#{id} </if> <if test="username!=null and username!=''"> and username like '%${username}%' </if> </where> </select>
提取where条件:
<sql id="query_user_where"> <if test="id!=null and id!=''"> and id=#{id} </if> <if test="username!=null and username!=''"> and username like '%${username}%' </if> </sql>
使用include引用:
<select id="selectUserByUser" parameterType="user" resultType="user"> select * from user <where> <include refid="query_user_where"/> </where> </select>
注:如果引用其他mapper.在引用xmlsql片段时,需要添加namespace,如下:
<includerefid="namespace.sql片段”/sql片段>
resultMap当输出pojo的字段与sql查询的字段名称不对应时,如果您想使用此pojo类作为输出类型,则需要使用resultmap。
此外,resultmap还解决了一对一关联查询、一对多关联查询等常见需求。
创建Person类:
public class Person { private int id; private String name;// 用户姓名,与User表的字段名称不同 private String sex;// 性别 private Date birthday;// 出生日期 private String addr;// 地址,与User表的字段名称不同 private String detail;// 详细信息 private Float score;// 成绩get//set。。。。。
resultmapp定义
mapper.resultMapp在xml文件中定义:
<!-- resultmap定义 --> <resultMap type="cn.itcast.mybatis.po.Person" id="personmap"> <id property="id" column="id"/> <result property="name" column="username" /> <result property="addr" column="address" /> </resultMap>
<id />:这个属性表示查询结果集的唯一标志,非常重要。如果多个字段是复合的唯一约束,则定义多个字段<id />。
Property:表示person属性。
Column:表示sql查询的字段名。
将Column和property放在一起,将sql查询的字段映射到指定的pojo属性上。
<result />:普通结果,即pojo属性。
这里只定义了sql查询到的字段与pojo属性名不一致,并通过后面的测试自动映射pojo属性名与sql字段相同。
Mapper.xml定义
<!-- 获取用户列表返回resultmapp --> <select id="selectUserListResultMap" resultMap="personmap"> select * from user </select>
使用resultmap指定上面定义的personmap。
Mapper接口定义
public List<Person> selectUserListResultMap() throws Exception;
实际返回类型为Person类型。
测试
public void testselectUserListResultMap() throws Exception{ //获得session SqlSession session = sqlSessionFactory.openSession(); //受限mapper接口实例 UserMapper userMapper = session.getMapper(UserMapper.class); User user = new User(); user.setUsername(管理员); ///查询用户列表返回resultmapp List<Person> list = userMapper.selectUserListResultMap(); System.out.println(list); //关闭sesssion session.close(); }
