MyBatis学习之一----基础了解
1、MyBatis
数据库有几个表,MyBatis就有多少个mapper.xml文件。可以更方便的对数据库进行增删改查。
MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。
根据上图来编写demo
导入依赖:
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.21</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.2.7</version> </dependency>
首先创建一个SqlMapConfig.xml,可以比喻为一个食品的配方,记录了连接数据库的信息,以及添加相应的mapper.xml配置文件。
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <!-- 和spring整合后 environments配置将废除 --> <environments default="development"> <environment id="development"> <!-- 使用jdbc事务管理 --> <transactionManager type="JDBC" /> <!-- 数据库连接池 --> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost:3306/zero?characterEncoding=utf-8" /> <property name="username" value="root" /> <property name="password" value="123" /> </dataSource> </environment> </environments> <!--配置Mapper的位置--> <mappers> <mapper resource="mapper.xml"/> </mappers> </configuration>
测试Mybatis的类
package com.back.mybatis; import com.back.pojo.User; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Test; import java.io.InputStream; /** * */ public class MybatisTest { @Test public void testMybatis()throws Exception{ //sqlSessionFactory // 指定一个xml文件位置 String resource = "SqlMapConfig.xml"; //读取文件的时候。默认就是从classes目录开始寻找 InputStream in = Resources.getResourceAsStream(resource); // 通过SqlSessionFactoryBuilder构建sqlSessionFactory SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in); // 从sqlSessionFactory中获取sqlSession SqlSession sqlSession = sqlSessionFactory.openSession(); //根据ID查询用户, // 第一个sql参数:指定sql的唯一标识:名称空间+.对应的sql的id的值 //第二个参数:传入参数 User user= sqlSession.selectOne("abc.selectUser",25); System.out.println(user); } }
首先读取SqlMapConfig.xml文件
然后通过Resources读取进来。
创建SqlSessionFactory工厂 //SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
从工厂中获取sqlSession来操作数据库,对其进行增删改查
mapper.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"> <!--namespace名称空间:当前的xml的唯一标识,只要满足所有mapper中的名称空间不相同即可--> <mapper namespace="abc"> <!--#{id}表示要接收的参数,相当于一个? id:当前的sql的唯一标识 resultType:结果集映射的对象的类型,默认的情况下需要书写对象的全路径 --> <select id="selectUser" resultType="com.back.pojo.User"> select * from user where id = #{id} </select> </mapper>
类似于Hibernate,需要创建一个pojo的类
User,实现其的getter和setter方法。
大概步骤:1、创建配置文件 SqlMapConfig.xml
2、获取SqlSessionFactory
3、获取sqlSession
4、通过sqlSession执行curd操作(需要提前创建mapper.xml(很多的statement /sql语句))并且在SqlMapConfig.xml中引入新创建的mapper(sql语句)
5、关闭连接sqlSession.commit()
6、关闭连接.session.close();
当User类里的username在数据库中是user_name时,可以使用别名
以上两种都可以
2、mybatis中dao层的命名规则
在mybatis中dao层的接口不再使用***Dao 而是修改成***Mapper
例如:UserDao,修改成UserMapper
思考:像userDao中的代码几乎都只有一行
@Override public void addUser(User user) { sqlSession.insert("user.addUser",user); } @Override public void deleteUserById(int id) { sqlSession.delete("user.deleteUser",id); }
其中的String和id就相当于statement和需要传入进去的参数
几乎都是固定的格式,那么是否可以不写了呢?
3、动态代理mapper实现类
需要名称空间为接口的全路径,某一个方法对应的方法名,要和select的id一致。满足这两个要求就可以实现动态代理的实现类。
<mapper namespace="com.back.dao.UserDaoService"> <select id="queryUserById" resultType="com.back.pojo.User"> select * from user where id = #{id} </select>
就可以不需要接口的实现类,而是直接使用test类来进行。
Namespace的作用:
Mapper中Namespace的定义本身是没有限制的,只要不重复即可,但是如果要想使用Mybatis提供的DAO的动态代理,namespace必须为DAO接口的全路径,例如:cn.mybatis.dao.UserDAO.
总结:
1)Mapper的namespace必须和mapper接口的全路径一致。
2)Mapper接口的方法名必须和sql定义的id一致。
3)Mapper接口中方法的输入参数类型必须和sql定义的parameterType一致(不一定)。
4)Mapper接口中方法的输出参数类型必须和sql定义的resultType一致。
public class UserDaoTest {
UserDaoService userDaoService;
SqlSession sqlSession;
@Before//执行Test方法之前执行
public void setUp()throws Exception{
String resouce="SqlMapConfig.xml";
InputStream inputStream= Resources.getResourceAsStream(resouce);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
sqlSession=sqlSessionFactory.openSession();
//userDaoService=new UserDaoImpl(sqlSession);
userDaoService=sqlSession.getMapper(UserDaoService.class);
}
4、mybatis-config配置,全局配置信息
configuration 配置
MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置(settings)和属性(properties)信息。文档的顶层结构如下:
- properties 属性
- settings 设置
- typeAliases 类型别名
- typeHandlers 类型处理器
- objectFactory 对象工厂
- plugins 插件
- environments 环境
- environment 环境变量
- transactionManager 事务管理器
- dataSource 数据源
- environment 环境变量
- databaseIdProvider 数据库厂商标识
- mappers 映射器
引入外部文件,properties
需要在configuration内的最上面。
4.1properties引入外部文件
<configuration> <!--引入外部的properties文件,resource指定文件所在路径--> <properties resource="jdbc.properties"></properties> <!-- 和spring整合后 environments配置将废除 --> <environments default="development"> <environment id="development"> <!-- 使用jdbc事务管理 --> <transactionManager type="JDBC" /> <!-- 数据库连接池 --> <dataSource type="POOLED"> <property name="driver" value="${driverClass}" /> <property name="url" value="${url}" /> <property name="username" value="${username}" /> <property name="password" value="${password}" /> </dataSource> </environment> </environments>
jdbc.properties
driverClass=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/zero username=root password=123
4.2settings
这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。下表描述了设置中各项的意图、默认值等。
4.2.2 mapUnderscoreToCamelCase 是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN 到经典 Java 属性名 aColumn 的类似映射。
java命名规则:驼峰书写,大小写区分两个单词的界限。
数据库的经典命名规则:两个单词之间使用下划线分割。user_name(因为有的数据库是不区分大小写的,所以在数据库中使用驼峰是没有意义的)
开启驼峰匹配,就相当于去掉数据库中的下划线,然后再与java中的属性名进行对应。
<settings> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings>
4.2.3 typeAliases
类型别名是为 Java 类型设置一个短的名字。它只和 XML 配置有关,存在的意义仅在于用来减少类完全限定名的冗余。
<typeAliases> <typeAlias alias="Author" type="domain.blog.Author"/> <typeAlias alias="Blog" type="domain.blog.Blog"/> <typeAlias alias="Comment" type="domain.blog.Comment"/> <typeAlias alias="Post" type="domain.blog.Post"/> <typeAlias alias="Section" type="domain.blog.Section"/> <typeAlias alias="Tag" type="domain.blog.Tag"/> </typeAliases>
type指定类的全路径;alias是别名,简写。
定义一个别名:
<typeAliases> <typeAlias type="com.back.pojo.User" alias="User"></typeAlias> </typeAliases>
为一个pojo类指定一个别名或者简称。然后再mapper.xml中可以直接书写简称即可。
<select id="queryUserById" resultType="User"> select * from user where id = #{id} </select>
缺点:需要为每一个类都定义一个类型别名。书写麻烦
也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:
<typeAliases>
<package name="domain.blog"/>
</typeAliases>
每一个在包 domain.blog 中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 domain.blog.Author 的别名为 author;若有注解,则别名为其注解值。
简称就是类的名字,并且不区分大小写。
所以不管写user还是User都可以,但是建议正常书写类的名字。
mybatis已经为普通的Java类型內建了许多相应的类型别名。它们都是大小写不敏感的。需要注意的是由于重载原始类型的名称所做的特殊处理。所以基本类型不需要书写全路径。
4.3 environments
1 <!-- 和spring整合后 environments配置将废除 --> 2 <environments default="development"> 3 <environment id="development"> 4 <!-- 使用jdbc事务管理 --> 5 <transactionManager type="JDBC" /> 6 <!-- 数据库连接池 --> 7 <dataSource type="POOLED"> 8 <property name="driver" value="${driverClass}" /> 9 <property name="url" 10 value="${url}" /> 11 <property name="username" value="${username}" /> 12 <property name="password" value="${password}" /> 13 </dataSource> 14 </environment> 15 </environments>
mybatis允许配置多个连接环境,default是选择加载哪个环境;
Mybatis允许配置多个环境,比如说开发环境、测试环境、生成环境,但是在构建SqlSessionFactory时只能选择一个。虽然这种方式也可以做到很方便的分离多个环境,但是在实际使用场景下 我们都是更多的使用Spring来管理数据源,做环境的分离。
4.4、mappers映射器
引入外部的,局部的mappers.xml。告诉MyBatis到哪里去找到这些语句。Java在自动查找这方面没有提供
<mappers> <mapper resource="org/mybatis/builder/AuthorMapper.xml"/> <mapper resource="org/mybatis/builder/BlogMapper.xml"/> <mapper resource="org/mybatis/builder/PostMapper.xml"/> </mappers>
resource:指定mapper所在文件路径
mapper中的url
<mappers> <mapper url="file:///var/mappers/AuthorMapper.xml"/> <mapper url="file:///var/mappers/BlogMapper.xml"/> <mapper url="file:///var/mappers/PostMapper.xml"/> </mappers>
从硬盘上找到xml文件,这种方式基本不用。因为只能在该硬盘上,被定死了。
使用mapper接口类的全路径。
<!-- Using mapper interface classes --> <mappers> <mapper class="org.mybatis.builder.AuthorMapper"/> <mapper class="org.mybatis.builder.BlogMapper"/> <mapper class="org.mybatis.builder.PostMapper"/> </mappers>
<mappers> <mapper resource="mapper.xml"/> <mapper resource="user.xml"/> <!--如果使用class方式映射的时候,要求: 1、mapper.xml和文件的名字和mapper接口的名字一致 2、要求mapper.xml文件和mapper接口类在一个目录下 --> <mapper class="com.back.dao.UserDaoService"/> </mappers>
但是有两个要求,否则是找不到mapper.xml文件的。
如果接口比较多,配置文件就需要配置多个。
注册所有的接口在一个目录下,
<!-- Register all interfaces in a package as mappers --> <mappers> <package name="com.mybatis.mapper"/> </mappers>
扫描指定包下面所有的接口,要求:1、要求mapper.xml文件的名字和mapper接口的名字,一致 2、要求mapper.xml文件和mapper接口类在一个目录下
但是就目前的方法来看还是没有实现xml和java代码分离。需要spring整合后来做处理。
4.5 mybatis-config.xml整理
1、properties标签-引入外部properties文件。在环境的位置可以使用${key}直接获取properties中的内容。
2、setting:开启驼峰匹配。
从数据的经典命名规则到java经典命名规则的映射;把数据库中的下划线去掉和java中的内容进行映射。
3、typealiases:
5、select和insert语句
如果是insert、update、delete操作,可以直接在方法上定义返回类型Integer。来接受一条sql语句改变的数据库的行数。
/** * 增加用户 */ public Integer addUser(User user);
测试代码中
Integer s=userDaoService.addUser(user1);
s即为数据库中收到影响的行数
如果想知道addUser的userID是多少可以使用主键回写功能。
<insert id="addUser" useGeneratedKeys="true" keyProperty="id" keyColumn="id">
INSERT INTO user (id, username, birthday, sex, address) VALUES (NULL, #{username}, NOW(), #{sex}, #{address});
</insert>
useGeneratedKeys:是否开启主键回写
keyProperty:java对象主键的属性名
keyColumn:数据库主键对应的字段的名字
代码
@Test public void TestAddUser(){ User user1=new User(); user1.setAddress("北京"); user1.setUsername("王强"); user1.setSex(2); userDaoService.addUser(user1); sqlSession.commit(); System.out.println(user1.getId()); }
6、#和$
占位符只能替代指定参数或要传入的值,不能替代表名或者SQL中的关键字。否则会报PersistenceException。
$属于SQL字符串的拼接。#是指占位符的形式。
$在取值的时候使用value表示传入的参数的值。所以mapper.xml中
<!--$在取值的时候使用value表示传入的参数的值--> <select id="queryUserByTableName" resultType="User"> select * from ${value} </select>
而如果写
<!--$在取值的时候使用value表示传入的参数的值--> <select id="queryUserByTableName" resultType="User"> select * from ${tableName} </select>
是会报错的。而且value这种方式不建议书写。因为如果后面有where value=${value}就无法识别谁代表谁
<!--在取值的时候使用@Param("tableName")为当前的参数指定一个名字--> <select id="queryUserById" resultType="User"> select * from user where id = #{id} </select>
public List<User> queryUserByTableName(@Param("tableName") String tableName);
7、mybatis传入多个参数parameterType传入参数
可以使用#{}进行接受一个变量,可以${}接受一个变量
如果${}没有指定参数名(在方法的参数列表中加入@Param)可以使用${value}表示传递过来的参数
使用#号的时候是与参数名无关的
1、使用$去传递参数的时候,使用@Param把参数指定一个名字
2、如果传入多个参数的时候,使用@Param,一定需要使用为每一个参数都去指定名字
<select id="queryUserById" resultType="User"> select * from user where id = #{idsfsdafd} </select>
传入多个参数
<!--$在取值的时候使用value表示传入的参数的值
如果传递多个参数,默认情况下是取不到值的
1、使用0、1等一些数字表示传入的参数的顺序并且取值
2、使用param+数字表示传入参数的次序,数字从1开始。
3、使用@param为参数起名字
-->
<select id="queryUserByTableName" resultType="User">
select * from ${tableName} where username=#{username} and password=#{password}
</select>
JAVA代码
public User login(@Param("username"))String username,@Param("password") String password);
推荐使用第三种
8、#{}与${}的区别
#是占位符
$是字符串的拼接
#可以随便写(和参数名无关)
$使用value取值,或者使用@Param指定一个名字再去取值
在Mybatis的mapper中,参数传递有2种方式,一种是#{}另一种是${},两者有着很大的区别。
#{}实现的 sql语句的预处理参数。之后执行sql中?号代替,使用时不需要关注数据类型,Mybatis自动实现数据类型的转换。并且可以防止SQL注入。
${}实现是sql语句的直接拼接,不做数据类型的转换,需要自行判断数据类型。不能防止SQL注入。
1、$字符串拼接,#参数占位相当于jdbc中的?
2、$不能够防止sql注入,#可以防止sql注入的。
3、$可以替换sql语句任何一个内容,#只能替换参数
4、$如果操作字符串,需要在sql中使用单引号。#不需要(不需要判断数据类型)
模糊查询
select * from user where name like '%张%';
分别用#和$完成一次
#
<select id="queryUserLikeName1" resultType="User"> select * from user where username like #{name} </select>
@Test public void queryUserLikeName1(){ List<User> users=userDaoService.queryUserLikeName1("%小%"); for(User user:users){ System.out.println(user); } }
$
<select id="queryUserLikeName1" resultType="User"> select * from user where username like '%${name}%' </select>
@Test public void queryUserLikeName1(){ List<User> users=userDaoService.queryUserLikeName1("明"); for(User user:users){ System.out.println(user); } }
#和$一个是占位符一个是字符串本身的链接操作
字符串的拼接操作不能防止SQL注入
那么什么时候用#什么时候用$?
不考虑SQL注入的时候使用$
如果传入的数据,不是sql中的参数的时候,不能够使用#。
通常使用#。
选择获取参数的时候,首要选择#的方式(1、可以防止SQL注入 2、可以不用考虑数据类型,简化书写3、sql是参数化的SQL,也是预编译的SQL,速度会快一些)
当#用不了的时候使用$。例如:sql需要改变的是内容是表名的时候,就可使用$方式。
9、ResultMap是Mybatis中最重要最强大的元素,使用它可以解决两大问题:
POJO属性名和表结构字段名不一致的问题(有些情况下也不是标准的驼峰格式)
完成高级查询,比如说,一对一、一对多、多对多。
解决表字段名和属性不一致的问题有两种方法:
1、如果是驼峰似的命名规则可以在Mybatis配置文件中设置<setting name="mapUnderscoreToCamelCase" value="true"/>解决
2、使用ResultMap解决。
<!--id:唯一标识 type:结果集对应的java类型 autoMapping:完成数据库字段名到属性名的自动映射,并且支持驼峰规则 --> <resultMap id="userResultMap" type="User" autoMapping="true"> <!--完成数据库字段名到java对象类型的属性的映射 id:指定主键的,建议不要省略 column:数据中字段的名字 property:java中字段的名字 --> <id column="id" property="id"/> <!--result完成部署主键的字段的映射--> <result column="user_name" property="username"/> </resultMap>
那么怎么给别人使用呢?
将
<select id="queryUserLikeName1" resultType="User">
select * from user where username like '%${name}%'
</select>
改为
<select id="queryUserLikeName1" resultMap="userResultMap">
select * from user where username like '%${name}%'
</select>
10、SQL片段
在java代码中会将公用的一些代码提取出来需要的地方直接调用方法即可,在Mybatis也是有类似的概念,那就是SQL片段。
在Mybatis中使用<sql id="">标签定义SQL片段,在需要的地方通过<include refid=""/>引用,例如:
<sql id="selectUser"> select * from user </sql> <select id="queryAllUser" resultType="com.back.pojo.User"> <include refid="selectUser"></include> where ....... </select>
如果我想让其他的mapper.xml文件也使用怎么办。
可以在外部创建一个xml文件,sql.xml
不和任何接口映射,只是引入进来。
sqlMapper.xl文件内容
<?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="abc"> <sql id="selectAllUser"> select * from user </sql> </mapper>
需要的时候
<select id="queryAllUser" resultType="com.back.pojo.User"> <include refid="abc.selectAllUser"></include> </select>
不需要在引入进来,只需要在sqlMapperConfig.xml中引入进来即可。
<!--配置Mapper的位置--> <mappers> <mapper resource="sqlMapper.xml"/> <mapper resource="mapper.xml"/> <mapper resource="user.xml"/> <!--如果使用class方式映射的时候,要求: 1、mapper.xml和文件的名字和mapper接口的名字一致 2、要求mapper.xml文件和mapper接口类在一个目录下 --> <!--<mapper class="com.back.dao.UserDaoService"/>--> </mappers>