为了解决JDBC缺点:业务代码和sql语句混在一起----解耦合、用起来太麻烦---专注sql语句编写
MyBatis可以:1)注册驱动 2)创建JDBC中使用的Connection、Statement、ResultSet 3)执行sql语句 4)处理ResultSet,把记录集中的数据转为Java对象,把该对象放入List集合
使用MyBatis:
- 使用 Maven 来构建普通Java项目,则需将MyBatis依赖、mysql驱动的依赖、maven插件放到pom.xml 文件中
拷贝路径XML的插件 <build> <resources> <resource> <directory>src/main/java</ 所在的目录 ----> < 包括目录下的 .properties,.xml 文件都会扫描到 ----> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>false</filtering> </resource> </resources> </plugins>
- 创建实体类(与表对应),属性名和数据库表的列名保持一致,生成属性对应的set、get方法
- 创建操作数据库的Dao接口,定义操作数据库的方法
- 创建接口:XxxDao
- 定义方法:定义操作----数据库表对应的对象(根据实体类创建的对象)的接口方法
- 在接口同目录下创建XML文件(mapper文件,写XML语句的),文件名和接口名保持一致。在文件里写SQL语句
<?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"> <!--.dtd约束文件:定义和显示当前文件可以使用的标签、属性等--> <mapper namespace="Dao接口的全名"> <!--mapper根标签,必须有值namespace命名空间:值推荐是Dao接口的全名,是参与识别sql语句的--> <!--写insert语句--> <insert> <!--写update语句--> <update> <!--写delete语句--> <delete> <!--写select语句--> <select> <!--执行SQL语句,把结果转成resultType对应的类型的对象(由框架通过反射机制创建resultType对应的类型的对象,把数据库中表的列赋值给与对象对象同名的属性)--> <select id="要执行的sql语句的唯一标识,推荐dao接口的方法名" parameterType="指定参数类型" resultType="告诉MyBatis执行语句后把数据赋值给哪个类型的Java对象(即步骤2的实体类),推荐用Java全类名"> <!--SQL语句, #{变量}是--执行sql语句是传入的实参--> select * from Blog where id = #{id} </select> <resultMap id="唯一值" type="执行SQL返回的Java类型全限定名称"> <!--自定义列名和属性名的对应--> <!--主键类型使用id标签--> <id column="表的列名" property="返回对象的属性值"/> <!--非主键类型使用result标签--> <result column="表的列名" property="返回对象的属性值"/> <!--属性名和列名一致不用定义--> </resultMap> <select id="" resultMap="resultMap标签的id"> select * from Blog where id = #{id} </select> </mapper>
- 创建MyBatis的主配置文件XML,只有一个,放到resources目录下:
- 获取数据库连接实例的数据源
- 指定其他mapper文件的位置
<?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> <!--设置日志功能--> <settings> ... <!--设置日志方式:SLF4J、LOG4J、LOG4J2、JDK_LOGGING、COMMONS_LOGGING、STDOUT_LOGGING、NO_LOGGING--> <setting name="logImpl" value="日志方式"/> ... </settings> <!--声明别名--> <typeAliases> <!--定义一个别名,方式一 type属性:Java全限定名称 alias属性:自定义别名--> <typeAlias type="全限定名" alias="别名"> <!--定义一个别名,方式二 (不区分大小写) 该包的下的类名就是别名--> <package name="包名" > </typeAliases> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <!--配置数据源:依赖创建连接对象Connection的--> <dataSource type="POOLED"> <!--driver:驱动类 相当于注册驱动--> <property name="driver" value="com.mysql.jdbc.Driver"/> <!--连接数据库的url--> <property name="url" value="jdbc:mysql://localhost:3306/数据库?useUnicode=true&characterEncoding=utf-8"/> <!--账号、密码--> <property name="username" value="账号"/> <property name="password" value="密码"/> </dataSource> </environment> </environments> <!--指定其他mapper文件的位置:为了找到其他文件中的sql语句--> <mappers> <!--一个mapper文件一个mapper标签--> <mapper resource="mapper文件的路径,从类路径target/classes路径开始"/> </mappers> </configuration>
- 使用:实现Dao接口,在实现方法中操作
-
定义mybatis主配置文件的文件 : String resource = "从类路径开始";
-
读取主配置文件: InputStream inputStream = Resources.getResourceAsStream(resource);
//Resources:MyBatis框架中的对象。作用:读取主配置文件信息 -
使用SqlSessionFactoryBuilder创建SqlSessionFactory对象
-
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//SqlSessionFactoryBuilder对象:负责创建SqlSessionFactory对象 -
每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心的。
// SqlSessionFactory对象:是一个重量级对象:创建该对象需要更多的资源和时间,在项目中有1个就可以
//SqlSessionFactory是一个接口:是SqlSession的工厂,作用是创建SqlSession对象 //实现类:DefaultSqlSessionFactory
方法:openSession():获取默认的SqlSession对象,默认是需要自动提交事务的
openSession(布尔值):true--->创建自动提交事务的SqlSession对象,flase等同于没有参数的SqlSession对象
-
-
从 SqlSessionFactory 中获取 SqlSession实例,SqlSession实例提供了执行SQL语句的方法
-
获取:SqlSession session = sqlSessionFactory.openSession()
//SqlSession:是一个接口,提供大量执行sql语句的方法 // 实现类:DefaultSqlSession,创建的对象不是线程安全的:使用步骤
1)在在方法内部执行sql语句之前先拿到SqlSession对象
2)调用SqlSession方法执行sql语句
3)关闭SqlSession对象
方法:selectOne():执行的sql语句最多得到一行记录,多于一个报错
selectList():执行sql语句,可以得到0或多行记录
selectMap():执行sql语句,返回一个Map结果
insert():执行insert语句
update():执行update语句
delete():执行delete语句
commit():提交事务
rollback():回顾事务
close(): 5关闭SqlSession对象
getMapper(dao接口.class):mybatis实现代理 -
指定要执行的SQL语句:String sqlId = namespace值+"."+SQL语句的id值
-
通过SqlSession实例执行SQL语句: 实体类型 对象 = (实体类型) session.selectOne(sqlId,传给占位符的实参值);//返回之前mapper配置的对象类型,用实体类去接受
MyBatis默认执行sql语句是手工提交事务模式,所以做insert、update、delete时需要提交事务:session.commit();
//对于insert语句:session.selectOne(sqlId,实体类型对象); //在实体类型对象中设置值,在SQL语句中用 #{属性名} 的方式来获取该值,即占位符中使用属性名 -
关闭SqlSession对象:session.close()
-
-
自动提交事务:当sql语句执行完毕后,提交事务。 //数据库更新的操作就保存到数据库了
手动提交事务:在你需要提交事务的位置,执行方法,提交事务或回顾事务
代理:
每次在Dao的方法里都需要固定的步骤,可以使用工具类封装操作
Dao的实现类其实并没有干什么实质性的工作,它仅仅就是通过 SqlSession的相关 API定位到映射文件 mapper中相应 id的 SQL语句,真正对 DB进行操作的工作其实是由框架通过 mapper中的 SQL完成的。
mybatis框架在执行期间,根据Dao接口,创建一个内存中的接口实现类对象,mybitis把这个技术叫做dao技术(dao的动态代理) //不用写dao接口的实现类了
dao技术:由mybatis创建Dao接口的实现类,使用框架创建的Dao实现类代替手工的dao实现类的功能(要求:mapper的namespace是接口的全限定名称、id是dao接口的方法名)
使用dao技术:SqlSession对象.getMapper(dao接口.class),返回该接口对应的dao对象 //即dao接口类型对象,等同于new出dao接口实现类
理解参数
如何通过Java程序把参数传入mapper文件中的SQL语句 : //参数:主要是指dao接口方法的形参
接口方法的参数:只有1个简单类型时 --------------->在SQL语句中使用: #{任意字符} //简单类型:Java基本类型、字符串
接口方法的参数:多个简单类型时------->@Param:命名参数,该注解由MyBatis提供,在接口方法的形参定义之前,定义参数名。该名可用在mapper文件SQL语句中 :#{别名}
例: 返回值 方法名(@Param(value = "别名")形参类型 形参名,@Param(value = "别名")形参类型 形参名)
形参一 形参二
接口方法的参数:1个对象(有属性、set、get)作为参数-------->在SQL语句中使用 :#{属性名} //mybatis是调用该属性的get、set方法去获取值
#{属性名}是简写 -------> #{ 属性名,javaType=java中数据类型全名 ,jdbcType=数据类型名称 }
接口方法的参数:多个简单类型参数 ------->按位置 #{agr0}代表参数1 #{arg1}代表参数2 #{arg2}代表参数3 …… //不推荐,可读性不好
接口方法的参数:1一个Map作为参数 ---------->在SQL语句中 通过key获取value值 #{key} //不推荐使用:参数个数、类型不清晰,可读性不好
parameterType:指定参数类型--->表示指定dao接口方法形参类型,给mybatis使用。 //可以使用Java类型的全限定名称 或 mybatis定义的别名
//mybatis通过反射机制可以获取接口方法的类型,所以也可以不写该属性
全限定名称<select id=" " resultType=" " parameterType="java.lang.Integer">
别名<select id=" " resultType=" " parameterType="别名">
占位符:#{字符} //使用jdbc对象是PrepareStatement对象,效率高,可以避免sql注入 。 //常作为列值使用,位于等号右侧,${ }位置的值和数据类型有关
${字符} 使用jdbc对象是Statement对象,效率低,字符串连接方式,有注入风险。 //${}位置的数据是原样使用的,不会区分数据类型,常用作表明、列名
封装MyBatis输出结果
封装输出: MyBatis执行SQL语句,得到ResultSet,转为Java对象
resultType属性:执行select时使用,作为<select>标签的属性出现
resultType:表示3种类型:
1)表示自定义对象 2)返回简单类型:执行SQL返回单个值 3)得到Map结构:列名key,列值value。推荐使用 Map<Object,Object>接受
resultType:表示结果类型,mybatis执行sql语句,得到的Java对象类型、简单类型、Map 它的值有2种表示方法,用来表示执行sql得到的值
//mybatis会调用该类型的无参构造创建对象,数据库表同名的列赋值给对象同名属性,得到Java对象。如果接口方法返回List集合,mybatis会把该对象放入List集合
- Java类型的全限定名称
- mybatis别名
resultMap:结果映射。自定义列名和Java对象属性的对应关系。(常用在列名和属性名不同时)
用法:
- 定义<resultMap id="唯一值" type="执行SQL返回的Java类型全限定名称">标签,指定列名和属性名的对应关系
自定义列名和属性名的对应(属性名和列名一致不用定义):
<id column="表的列名" property="返回对象的属性值"/> //主键类型使用id标签
<result column="表的列名" property="返回对象的属性值"/> //非主键类型使用result标签- 在<select>标签使用resultMap属性指定:上面定义的<resultMap>标签的id值:
<select id="" resultMap="resultMap标签的id">
自定义别名:mybatis提供的对Java类型定义的好记的名字
步骤:
- 主配置文件:使用<typeAlias>标签声明别名
- 在mapper文件:resultType="别名"
列名和属性名不太一致问题:
- 使用resultMap
- 使用 resultType,让SQL的列名使用SQL别名,让SQL别名和属性名一致
like:模糊查询
方式一:在Java程序中,把SQL语句中的like内容(即like的条件)组装好,传入sql语句 //推荐
法式二:直接在SQL语句组装
动态SQL:同一个dao的方法,根据不同的条件可以表示不同的SQL语句,主要是where的变化
mybatis通过提供的标签,来实现动态SQL:if、where、foreach、sql
使用动态sql,dao方法的形参使用Java对象
if标签语法 //问题:若所有if条件都不满足,则SQL语句会发生什么问题,怎样解决----WHERE关键字的问题
<if text="boolean判断结果,使用Java对象的属性">
SQL语句
</if>
mapper文件中:
<select id="selectStudentIf" resultType="com.bjpowernode.domain.Student">
select id,name,email,age from student
where 1=1
<if test="name != null and name !='' ">
and name = #{name}
</if>
<if test="age > 0 ">
and age > #{age}
</if>
</select>
where标签语法:里面有一个或多个if标签,当有一个if判断为true,where会转为WHRTR关键字附加到SQL后,若所有if都不为true,则忽略where和if
<where>
<if></if>
</where>
<select id="selectStudentWhere" resultType="com.bjpowernode.domain.Student">
select id,name,email,age from student
<where>
<if test="name != null and name !='' ">
and name = #{name}
</if>
<if test="age > 0 ">
and age > #{age}
</if>
</where>
</select>
foreach标签:循环,可以循环数组、list集合,一般在in语句中
<foreach collection="表示要遍历对象类型" //表示要遍历对象类型,却决于形参类型 数组-->array 集合--->list
open="遍历开始之前添加的字符"
close="遍历结束之后添加的字符"
item="集合中的成员,自定义的变量"
separator="集合成员之间添加的分隔符
">
#{item属性的值}
</foreach>
方式一:遍历 List --->简单类型
<select id="selectStudentForList" resultType="com.bjpowernode.domain.Student">
select id,name,email,age from student
<if test="list !=null and list.size > 0 "> //传入的集合不为空且长度大于0
where id in
<foreach collection="list" open="(" close=")" item="stuid" separator=",">
#{stuid}
</foreach>
</if>
</select>
方式二:遍历 List --->对象类型
<select id="selectStudentForList2" resultType="com.bjpowernode.domain.Student">
select id,name,email,age from student
<if test="list !=null and list.size > 0 ">
where id in
<foreach collection="list" open="(" close=")" item="stuobject" separator=",">
#{stuobject.id}
</foreach>
</if>
</select>
sql标签:表示一段代码片段 表名、字段、where条件都可以,可以在其他地方复用sql标签的内容
1)在mapper文件中定义sql标签的代码片段
<sql id="片段的自定义名称,唯一性">
sql语句片段
<sql>2)在要用的位置使用include标签引用代码片段 //可以在任意SQL语句位置中使用片段
<include refid="sql片段的id"/>
<sql id="studentSql">
select id,name,email,age from student
</sql>
<select id="selectStudentSqlFragment" resultType="com.bjpowernode.domain.Student">
<!-- 引用sql片段 -->
<include refid="studentSql"/>
<if test="list !=null and list.size > 0 ">
where id in
<foreach collection="list" open="(" close=")" item="stuobject" separator=",">
#{stuobject.id}
</foreach>
</if>
</select>
配置文件:
1)主配置文件:提供全局设置-----日志、数据源、mapper文件的位置
2)mapper文件:写sql语句的主配置文件:
<propertys resource="类路径下的配置文件"> 使用外部数据库配置文件 //这样<property>就可以使用外部的数据了
<settings> 全局设置,影响整个mybatis运行
<typeAliases> 别名设置
<environments default=“某个环境的id值”> 配置环境 //环境标签,里面可以配置多个<environment> default表示默认连接的数据库
<environment id="id 定义环境的标识"> 每个表示一个数据库的连接信息
<transactionManager type="表示事务管理器类型"> 事务管理器
type属性值:JDBC--表示使用Connection对象,由mybatis自己完成事务处理
MANAGED---把事务的管理交给容器来做(由其他软件完成事务的提交回滚)
<dataSource type="数据源类型"> 数据源,用来创建Connection对象,连接数据库
type属性值:POOLED---mybatis会在内存中创建PooledDataSource类,管理多个Connection连接对象,使用连接池 //基本都是用这个
UNPOOLED---不使用连接池,MyBatis 会创建 UnpooledDataSource类,每次执行sql语句先创建Connection对象,执行完sql在关闭Connection对象
JNDI---Java的命名和目录服务 MyBatis会从 JNDI服务上查找 DataSource 实例,然后返回使用 //很老了,不会有
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/数据库?useUnicode=true&characterEncoding=utf-8"/>
<property name="username" value="账号"/>
<property name="password" value="密码"/>
或 引用外部文件---使用外部文件的值,前提是要配置好<propertys>
<property name="driver" value="${对应的key值}"/> /
<property name="url" value="${key值}"/>
<property name="username" value="${key值}"/>
<property name="password" value="${key值}"/>
<mappers>
<mapper resource="mapper路径文件"> 指定其他mapper文件位置
<package name="mapper文件所在的包名" /> 该包下所有mapper文件一次加载 //需要mapper文件和dao接口在同一目录、mapper文件和dao接口名称一致
有可能<dataSource>内的的数据库配置信息太长,为了方便管理,另外放一个独立文件去:
1.在resources目录中,创建xxx.properties文件,key=value //例如:jdbc.url=xxxxxxx .是为了看起来更有层次,每别的意思
2.在主配置文件使用:<property name="driver" value="${key值}"/>