mybatis 经过 简单的xml 或注解的方式将要执行的各类 statement 配置起来,并经过 java 对象(POJO类)和 statement 中sql 的动态参数进行映射生成最终执行的 sql 语句,最后由 mybatis 框架执行 sql 并将结果映射为 java 对象并返回。java
1.未经封装,使用jdbc原始方法(加载驱动,获取链接)进行对数据库数据的crud操做mysql
2.数据库链接频繁建立、释放,形成系统资源的浪费,影响系统性能,web
解决:可以使用数据库链接池技术
3.Sql语句在代码中属于硬编码,而实际应用sql语句常常变化,需改变java代码,不利于代码维护sql
解决:将 Sql 语句配置在 XXXXmapper.xml 文件中与 java 代码分离。
4.使用prepareStatement向占位符传参存在硬编码,由于参数不定,条件可多可少,需修改代码不易维护。数据库
解决:Mybatis 自动将 java 对象映射至 sql 语句,经过 statement 中的 parameterType 定义输入参数的类型。
5.结果集解析存在硬编码(查询列名),若sql变化(参数),致使解析代码变化,系统不易维护,可将数据库记录封装为POJO类对象。windows
解决:Mybatis 自动将 sql 执行结果映射至 java 对象,经过 statement 中的 resultType 定义输出结果的类型
<?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属性为dao接口的全限定类名--> <mapper namespace="com.itheima.dao.IUserDao"> <!--配置查询全部--> <select id="findAll" resultType="com.itheima.domain.User"> select * from user </select> </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"> <!-- mybatis的主配置文件 --> <configuration> <!-- 配置环境 --> <environments default="mysql"> <!-- 配置mysql的环境--> <environment id="mysql"> <!-- 配置事务的类型--> <transactionManager type="JDBC"></transactionManager> <!-- 配置数据源(链接池) --> <dataSource type="POOLED"> <!-- 配置链接数据库的4个基本信息,建立链接对象 --> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/eesy_mybatis"/> <property name="username" value="root"/> <property name="password" value="1234"/> </dataSource> </environment> </environments> <!-- 指定映射配置文件的位置,映射配置文件指的是每一个dao独立的配置文件 --> <mappers> <!-- 基于xml --> <mapper resource="com/itheima/dao/IUserDao.xml"/> <!-- 基于注解 --> <mapper class="com.itheima.dao.IUserDao.xml"/> </mappers> </configuration>
public class MybatisTest { /** * 入门案例 * @param args */ public static void main(String[] args)throws Exception { //1.读取配置文件 InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml"); //2.建立SqlSessionFactory工厂(不是本身建立的,构建者模式) SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); SqlSessionFactory factory = builder.build(in); //3.使用工厂生产SqlSession对象 SqlSession session = factory.openSession(); //4.使用SqlSession建立Dao接口的代理对象 IUserDao userDao = session.getMapper(IUserDao.class); //5.使用代理对象执行方法 List<User> users = userDao.findAll(); for(User user : users){ System.out.println(user); } //6.释放资源 session.close(); in.close(); } }
第一个:使用类加载器,只能读取类路径的配置文件设计模式
第二个:使用servletContext对象的getRealPath()缓存
优点:把对象的建立细节隐藏,使使用者直接调用方法便可拿到对象安全
优点:解耦,下降类之间的依赖。使用工厂生产对象,解决类之间的依赖,web开发每次修改改动源码,影响开发效率,使用工厂生产对象,不须要从新编译,部署,启动服务器。服务器
优点:在不修改源码基础上对已有方法进行加强
加强对象的功能:
设计模式:一些通用的解决固定问题的方式
1)装饰模式
2)代理模式
静态代理:有一个类文件描述代理模式
动态代理:在内存中造成代理类
tips:使用jdbc链接数据库执行sql语句时(执行许多SQL语句的JDBC程序产生大量的Statement和PreparedStatement对象。)
1.当sql语句含有多个参数,屡次使用时,使用preparestament将sql进行初始化,提到数据库中进行预编译,提升效率;
2.能够替换变量,sql语句能够包含占位符?,把?替换成变量,ps.setString()....
3.安全性,有效防止SQL注入的问题:传递给PreparedStatement对象的参数能够被强制进行类型转换,使开发人员能够确保在插入或查询数据时与底层的数据库格式匹配。(不太懂)
总结:
PreparedStatement: 数据库会对sql语句进行预编译,下次执行相同的sql语句时,数据库端不会再进行预编译了,而直接用数据库的缓冲区,提升数据访问的效率(但尽可能采用使用?号的方式传递参数),若是sql语句只执行一次,之后再也不复用。 从安全性上来看,PreparedStatement是经过?来传递参数的,避免了拼sql而出现sql注入的问题,因此安全性较好。
在开发中,推荐使用 PreparedStatement
所谓SQL注入,就是经过把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。具体来讲,它是利用现有应用程序,将(恶意)的SQL命令注入到后台数据库引擎执行的能力,它能够经过在Web表单中输入(恶意)SQL语句获得一个存在安全漏洞的网站上的数据库,而不是按照设计者意图去执行SQL语句。
怎么防止SQL注入,使用存储过程来执行全部的查询;检查用户输入的合法性;将用户的登陆名、密码等数据加密保存。
至关于占位符,里面内容为基本类型时随意写,若参数为对象,则#{}里使用ognl(对象图导航语言)表达式,语法格式为#{对象.对象}的方式
{user.username}它会先去找 user 对象,而后在 user 对象中找到 username 属性,并调用getUsername()方法把值取出来。可是咱们在 parameterType 属性上指定了实体类名称,因此能够省略 user.而直接写 username。
添加记录,使用insert标签。只有参数,没有返回值。
若是Mysql数据想获取当前记录的ID,须要配置:
selectKey
当运行了添加的方法后,mybatis会自动将返回的ID,设置给参数对象parameterType配置的对象
<insert id="saveUser" parameterType="com.itheima.domain.User"> <!-- 配置插入操做后,获取插入数据的id --> <selectKey keyProperty="id" keyColumn="id" resultType="int" order="AFTER"> select last_insert_id(); </selectKey> insert into user(username,address,sex,birthday)values(#{username},#{address},#{sex},#{birthday}); </insert>
咱们在配置文件中没有加入%来做为模糊查询的条件,因此在传入字符串实参时,就须要给定模糊查询的标
识%。 配置文件中的#{username}也只是一个占位符,因此 SQL 语句显示为“?”。
模糊查询另外一种查询方式:'%${value}%' 代替#{}
#{}表示一个占位符号 经过#{}能够实现 preparedStatement 向占位符中设置值,自动进行 java 类型和 jdbc 类型转换, #{}能够有效防止 sql 注入。 #{}能够接收简单类型值或 pojo 属性值。 若是 parameterType 传输单个简单类 型值, #{}括号中能够是 value 或其它名称。 ${}表示拼接 sql 串 经过${}能够将 parameterType 传入的内容拼接在 sql 中且不进行 jdbc 类型转换, ${}能够接收简 单类型值或 pojo 属性值,若是 parameterType 传输单个简单类型值, ${}括号中只能是 value。
parameterType:基本类型与string,采用包名.类名,如Java.Lang.String
包装类采用全限定分类名
特殊状况:实体类属性名称与数据库列名不一致,查询结果为null
MySql在windows系统中不区分大小写,映射配置修改以下:
<!-- 配置查询全部操做 --> <select id="findAll" resultType="com.itheima.domain.User"> select * from user </select>
改成:
使用别名查询 <!-- 配置查询全部操做 --> <select id="findAll" resultType="com.itheima.domain.User"> select id as userId,username as userName,birthday as userBirthday, sex as userSex,address as userAddress from user </select>
思考:
若是咱们的查询不少,都使用别名的话写起来岂不是很麻烦,有没有别的解决办法呢? 以下
<!-- 创建 User 实体和数据库表的对应关系 type 属性:指定实体类的全限定类名 id 属性:给定一个惟一标识,是给查询 select 标签引用用的。 --> <resultMap type="com.itheima.domain.User" id="userMap"> <id column="id" property="userId"/> <result column="username" property="userName"/> <result column="sex" property="userSex"/> <result column="address" property="userAddress"/> <result column="birthday" property="userBirthday"/> </resultMap> id 标签:用于指定主键字段 result 标签:用于指定非主键字段 column 属性:用于指定数据库列名 property 属性:用于指定实体类属性名称
对应映射配置
<!-- 配置查询全部操做 --> <select id="findAll" resultMap="userMap"> select * from user </select>
properties标签:需把jdbcConfig.properties提取出来,放到resources路径下
typeAliases标签:
pakage标签:
<!-- mybatis的主配置文件 --> <configuration> <properties resource="jdbcConfig.properties"></properties> <!--使用typeAliases配置别名,它只能配置domain中类的别名 --> <typeAliases> <!-- 用于指定要配置别名的包,当指定以后,该包下的实体类都会注册别名,而且类名就是别名,再也不区分大小写--> <package name="com.itheima.domain"></package> </typeAliases> <!-- 配置环境 --> <environments default="mysql"> <!-- 配置mysql的环境--> <environment id="mysql"> <!-- 配置事务的类型--> <transactionManager type="JDBC"></transactionManager> <!-- 配置数据源(链接池) --> <dataSource type="POOLED"> <!-- 配置链接数据库的4个基本信息 --> <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> <!-- 指定映射配置文件的位置,映射配置文件指的是每一个dao独立的配置文件 --> <mappers> <!-- package标签是用于指定dao接口所在的包,当指定了以后就不须要在写mapper以及resource或者class了 --> <package name="com.itheima.dao"></package> </mappers> </configuration>
动态SQL目的是为了更好的重用SQL语句的片断或者灵活的生成SQL语句。本质上,就是SQL语句字符串的拼接。
if进行判断,test属性为true,就拼接上标签中的SQL语句 test中就是OGNL表达式 1. 不要加 #{} 2. 逻辑运算,要使用 and,or 3. 调用对象的方法: list.size() <select id="findUserByCondition" resultMap="userMap" parameterType="user"> select * from user <where> <if test="userName != null"> and username = #{userName} </if> <if test="userSex != null"> and sex = #{userSex} </if> </where> </select>
<update id="updateUser" parameterType="user"> update user <set> <if test="username !=null and username !=''"> username = #{username} , </if> <if test="username !=null"> sex = #{sex}, </if> <if test="username !=null"> password = #{password}, </if> </set> where id=#{id} </update>
foreach 进行循环的集合,必须经过包装的对象提供。 <select id="findInIds" resultType="user" parameterType="queryvo"> <where> <if test="ids != null and ids.size() > 0"> <foreach collection="ids" open="and id in (" close=")" item="uid" separator=","> #{uid} </foreach> </if> </where> </select>
将SQL中,相同的内容提取出来,在多个地方进行引用。 1. SQL语句中的字符串 2. 动态SQL中的标签 抽取SQL语句 <sql id="defaultUser"> select * from user </sql> 使用SQL <select id="findAll" resultMap="userMap"> <include refid="defaultUser"></include> </select>
简化sql片断
<!-- 抽取重复的语句代码片断 --> <sql id="defaultSql"> select * from user </sql> <!-- 配置查询全部操做 --> <select id="findAll" resultType="user"> <include refid="defaultSql"></include> </select>
使用 resultMap,定义专门的 resultMap 用于映射一对一查询结果。
经过面向对象的(has a)关系能够得知,咱们能够在 Account 类中加入一个 User 类的对象来表明这个帐户
是哪一个用户的。
<resultMap id="accountUserMap" type="account"> <id property="id" column="aid"></id> <result property="uid" column="uid"></result> <result property="money" column="money"></result> <!-- 一对一的关系映射:配置封装user的内容--> <association property="user" column="uid" javaType="user"> <id property="id" column="id"></id> <result column="username" property="username"></result> <result column="address" property="address"></result> <result column="sex" property="sex"></result> <result column="birthday" property="birthday"></result> </association> </resultMap>
<resultMap type="user" id="userMap"> <id column="id" property="id"></id> <result column="username" property="username"/> <result column="address" property="address"/> <result column="sex" property="sex"/> <result column="birthday" property="birthday"/> <!-- collection 是用于创建一对多中集合属性的对应关系 ofType 用于指定集合元素的数据类型 --> <collection property="accounts" ofType="account"> <id column="aid" property="id"/> <result column="uid" property="uid"/> <result column="money" property="money"/> </collection> </resultMap> <!-- 配置查询全部操做 --> <select id="findAll" resultMap="userMap"> select u.*,a.id as aid ,a.uid,a.money from user u left outer join account a on u.id =a.uid </select> </mapper> collection 部分定义了用户关联的帐户信息。表示关联查询结果集 property="accList": 关联查询的结果集存储在 User 对象的上哪一个属性。 ofType="account": 指定关联查询的结果集中的对象类型即 List中的对象类型。此处可使用别名,也可使用全限定名。
<!--定义 role 表的 ResultMap--> <resultMap id="roleMap" type="role"> <id property="roleId" column="rid"></id> <result property="roleName" column="role_name"></result> <result property="roleDesc" column="role_desc"></result> <collection property="users" ofType="user"> <id column="id" property="id"></id> <result column="username" property="username"></result> <result column="address" property="address"></result> <result column="sex" property="sex"></result> <result column="birthday" property="birthday"></result> </collection> </resultMap> <!--查询全部--> <select id="findAll" resultMap="roleMap"> select u.*,r.id as rid,r.role_name,r.role_desc from role r left outer join user_role ur on r.id = ur.rid left outer join user u on u.id = ur.uid </select> </mapper>
//SqlMapConfig.xml <!--配置参数--> <settings> <!--开启Mybatis支持延迟加载--> <setting name="lazyLoadingEnabled" value="true"/> <!-- 默认就是true --> <setting name="aggressiveLazyLoading" value="false"></setting> </settings>
2.一对一
<!-- 一对一的关系映射:配置封装user的内容 select属性指定的内容:查询用户的惟一标识:当前mapper配置的sid(namespace+id) column属性指定的内容:用户根据id查询时,所须要的参数的值 --> <association property="user" column="uid" javaType="user" select="com.itheima.dao.IUserDao.findById"></association> <select id="findAll" resultMap="accountMap"> select * from account </select> select: 填写咱们要调用的 select 映射的 id column : 填写咱们要传递给 select 映射的参数
3.一对多
<!-- collection 是用于创建一对多中集合属性的对应关系 ofType 用于指定集合元素的数据类型 select 是用于指定查询帐户的惟一标识(帐户的 dao 全限定类名加上方法名称) column 是用于指定使用哪一个字段的值做为条件查询 --> <collection property="accounts" ofType="account" select="com.itheima.dao.IAccountDao.findByUid" column="id"> </collection> <!-- 配置查询全部操做 --> <select id="findAll" resultMap="userMap"> select * from user </select> <collection>标签: 主要用于加载关联的集合对象 select 属性: 用于指定查询 account 列表的 sql 语句,因此填写的是该 sql 映射的 id column 属性: 用于指定 select 属性的 sql 语句的参数来源,上面的参数来自于 user 的 id 列,因此就写成 id 这一 个字段名了
缓存的使用
缓存的做用和缓存使用的时机
mybatis的缓存
一级缓存
SqlSession范围的缓存,同一个SQLSession对象,可使用同一个缓存。当使用session查询相同的数据(同一方法,相同参数),查询出同一个对象。
当session关闭,调用clearCache()方法,执行增删改操做时,会被清空。
二级缓存
SQLSessionFactory范围的缓存,通常应用只会建立一个SQLSessionFactory对象,当前应用中,全部的操做均可以使用二级缓存的数据。不一样的session查询相同的数据(同一方法,相同参数)能够从二级缓存中获取,二级缓存只缓存了数据的内容,每一个session会拿到不一样的对象,拥有相同的属性。
二级缓存默认是不启用的,须要手动开启。
//SqlMapConfig.xml <settings> <setting name="cacheEnabled" value="true"/> </settings> //对应的mapper文件中 <cache /> //当前配置的mapper文件的对应须要缓存的SQL上 <!-- 根据id查询用户 --> <select id="findById" parameterType="INT" resultType="user" useCache="true"> select * from user where id = #{uid} </select>
@Insert:实现新增
@Update:实现更新
@Delete:实现删除
@Select:实现查询
@Result:实现结果集封装
@Results:能够与@Result 一块儿使用,封装多个结果集
@ResultMap:实现引用@Results 定义的封装
@One:实现一对一结果集封装
@Many:实现一对多结果集封装
@SelectProvider: 实现动态 SQL 映射
@CacheNamespace:实现注解二级缓存的使用
搭建开发环境
若是使用了注解开发,就不能再使用xml配置。
单表增删改查操做
对象属性和表字段不一致的配置
@Select("select * from user") @Results(id="userMap",value={ @Result(id=true,column = "id",property = "userId"), @Result(column = "username",property = "userName"), @Result(column = "address",property = "userAddress"), @Result(column = "sex",property = "userSex"), @Result(column = "birthday",property = "userBirthday") }) List<User> findAll();
//重复使用resultmap @Select("select * from user where id=#{id} ") @ResultMap("userMap") User findById(Integer userId);
4.对象关系映射配置
一对一
@Select("select * from account") @Results(id="accountMap",value = { @Result(id=true,column = "id",property = "id"), @Result(column = "uid",property = "uid"), @Result(column = "money",property = "money"), @Result(property = "user",column = "uid",one=@One(select="com.itheima.dao.IUserDao.findById",fetchType= FetchType.EAGER)) }) //property 对象属性的名称 //column 后面配置的select查询的参数 //one 属性是对象 // select 查询的方法 // fetchType 是否延迟加载 FetchType.EAGER 当即加载 List<Account> findAll();
2.一对多
@Select("select * from user") @Results(id="userMap",value={ @Result(id=true,column = "id",property = "userId"), @Result(column = "username",property = "userName"), @Result(column = "address",property = "userAddress"), @Result(column = "sex",property = "userSex"), @Result(column = "birthday",property = "userBirthday"), @Result(property = "accounts",column = "id", many = @Many(select = "com.itheima.dao.IAccountDao.findAccountByUid", fetchType = FetchType.LAZY)) }) // many 对象的属性是集合 fetchType = FetchType.LAZY 延迟就加载 List<User> findAll();