MyBatis
MyBatis
-
什么是框架?
-
它是一个半成品软件,将所有的公共的、重复的功能解决掉,帮助程序快速高效的进行开发,它是可复用的、可扩展的。
-
-
什么是MyBatis框架?
-
MyBatis本是 apache 的一个开源项目 iBatis,2010年这个项目由 apache software foundation 迁移到了 google code,并且改名为MyBatis。2013年迁移到GitHub。
-
MyBatis完成数据库访问层的优化,它专注于sql语句,简化了过去JDBC繁琐的访问机制。
-
-
代理模式
-
无法访问目标对象,通过代理对象进行访问,而且增强式的访问,适合进行业务的扩展。
-
功能:增强功能;限制目标对象的访问。
-
-
代理模式的分类
-
静态代理
-
动态代理,又称为JDK动态代理,CGLib动态代理(子类代理)
-
-
面向接口编程(重要)
-
类中的成员变量设计为接口,方法的形参设计为接口,方法的返回值设计为接口,调用时接口指向实现类。
-
-
动态代理
-
代理对象在程序运行的过程中动态在内存构建,可以灵活的进行业务功能的切换。
-
JDK动态代理类型:class com.sun.proxy.$Proxy
-
-
什么是静态代理
-
特点:
-
目标对象和代理对象实现同一个业务接口。
-
目标对象必须实现接口。
-
代理对象在程序运行前就已经存在。
-
能够灵活的进行目标对象的切换,却无法进行功能的灵活处理。(使用动态代理解决)
-
-
-
JDK动态代理
-
目标代理对象必须实现业务接口(目标对象必须实现接口,才可以使用JDK的动态代理)
-
JDK代理对象不需要实现业务接口
-
JDK动态代理的对象在程序运行前不存在,在程序运行时动态的在内存中构建。
-
JDK动态代理灵活的进行业务功能的切换。
-
本类中的方法(非接口中的方法)不能被代理。
-
-
CGLib动态代理
-
又称为子类。动态的在内存中构建子类对象,重写父类的方法进行代理功能的增强。
-
如果目标对象没有实现接口,则只能通过CGLib子类代理进行功能增强。
-
子类代理是通过字节码框架ASM来实现的。
-
注意:被代理类和方法不能使用final。
-
-
JDK动态代理用到的类和接口
-
Proxy类
-
它是java.lang.reflect.Proxy包下的类。它有一个方法Proxy.newProxyInstance(...)专门用来生成动态代理对象。
public static Object newProxyInstance( ClassLoader loader, //类加载器 Class<?>[] interfaces, //目标对象实现的所有接口 InvocationHandler h //代理的功能和目标对象的业务功能调用 ) throws IllegalArgumentException {...}
-
-
Method类
-
反射用的类,用来进行目标对象方法的反射调用。
-
-
InvocationHandler接口
-
它是实现代理和业务功能的,我们在调用时使用匿名内部实现。
public Object getAgent(){ return Proxy.newProxyInstance( //ClassLoader loader,类加载器,完成目标对象的加载 target.getClass().getClassLoader(), //Class<?>[] interfaces,目标对象实现的所有接口 target.getClass().getInterfaces(), //InvocationHandler h,实现代理功能的接口,匿名内部类实现 new InvocationHandler(){ public Object invoke( //创建代理对象 Object proxy, //method就是目标方法 Method method, //目标方法的参数 Object[] args) throws Throwable{ //代理功能 System.out.println("代理功能开始"); //主业务功能实现 Object obj = method.invoke(target,args); //代理功能 System.out.println("代理功能结束"); return obj; //这个是目标方法的返回值 } } ) }
-
-
-
面向接口编程
-
类中的成员方法设计成接口
-
方法的参数设计为接口
-
方法的返回值设计为接口
-
使用时使接口指向实现类
-
-
什么是三层架构?
-
界面层:和用户打交道的,接收用户的请求参数,显示处理结果。(jsp、html、servlet)
-
业务逻辑层:接收了界面传递的数据,计算逻辑,调用数据库,获取数据。
-
数据访问层:访问数据库,执行对数据的查询、修改、删除等。
-
注意:各层之间的调用顺序是固定的,不允许跨层访问。
-
-
三层对应的包及处理框架
-
界面层:controller(servlet) --SpringMVC
-
业务逻辑层:service(xxxService类) --Spring
-
数据访问层:dao包(xxxDao类) --MyBatis
-
-
三层中的架构
-
用户使用界面层 -- 业务逻辑层 -- 数据访问层(持久层) -- 数据库(MySQL)
-
-
为什么要使用三层?
-
结构清晰,耦合度低,各层分工明确。
-
可维护性高,可扩展性高。
-
有利于标准化。
-
开发人员可以只关心结构中的其中某一层功能的实现。
-
有利于各层逻辑的复用。
-
-
SqlSessionFactory
//读取mybatis-config.xml文件 InputStream in = Resources.getResourceAsStream("mybatis-config.xml"); //初始化mybatis,创建SqlSessionFactory类的实例 SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in); //创建SqlSession实例 SqlSession session = SqlSessionFactory.openSession();
-
@Befor注解
@Befor //所有的@Test方法执行前执行的代码 public void openSqlSession() throws IOException{ //使用文件流读取核心配置文件sqlMapConfig.xml InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml"); //创建SqlSessionFactory工厂 SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in); //取出sqlSession的对象 sqlSession = factory.openSession(); }
-
@After注解
public void closeSqlSession(){ //关闭sqlSession sqlSession.close(); }
-
为实体类注册别名
-
单个注册
<typeAlias type="com.myself.pojo.Student" alias="stu"></typeAlias>
-
批量注册
<typeAliases> <package name="com.myself.pojo"/> </typeAliases>
-
-
配置环境变量
<!--配置环境变量--> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"></transactionManager> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> </environments>
-
指定资源位置
<build> <resources> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.xml</include> <include>**/*.properties</include> </includes> <filtering>false</filtering> </resource> <resource> <directory>src/main/java</directory> <includes> <include>**/*.xml</include> <include>**/*.properties</include> </includes> <filtering>false</filtering> </resource> </resources> </build>
-
动态代理存在的意义
-
实现了三个模块组的解耦,可以使它们可以在客户端各自可以独立的变化,为客户端的调用提供了极大的方便。通过这种方式,被代理的对象可以在运行时动态改变,需要控制的接口可以在运行时改变,控制的方式也可以动态改变,从而实现了非常灵活的动态代理关系。
-
-
动态代理的实现规范
-
UsersMapper.xml文件与UsersMapper.java的接口必须在同一个目录下。
-
UsersMapper.xml文件与UsersMapper.java的接口的文件名必须一致。
-
UsersMapper.xml文件中标签的id值与UsersMapper.java的接口中方法的名称完全一致。
-
UsersMapper.xml文件中标签的parameterType属性值与UsersMapper.java的接口中的方法的参数类型完全一致。
-
UsersMapper.xml文件中标签的resultType值与UsersMapper.java的接口中的方法的返回值类型完全一致。
-
UsersMapper.xml文件中namespace属性必须是接口的完全限定名称com.myself.mapper.UsersMapper.
-
在SqlMapConfig.xml文件中注册mapper文件时,使用class=接口的完全限定名称com.myself.mapper.UsersMapper.
-
-
设置日志输出
<settings> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings>
-
#{}占位符
-
传参大部分使用#{}传参,它的底层使用的是PreparedStatement对象,是安全的数据库访问,防止sql注入。
-
#{}里面如何写,看parameterType参数的类型:
-
如果parameterType的类型是简单类型,则#{}里面随便写。
<select id="getById" parameterType="int" resultType="users"> -->入参是简单类型 select id,username from users where id=#{tzw} -->随便写 </select>
-
如果parameterType的类型是实体类的类型,则#{}里面只能是类中成员变量的名称,而且区分大小写。
<insert id="insert" parameterType="users"> -->入参是实体类 insert into users(username)values(#{userName}) </insert>
-
-
-
${}字符串拼接或字符串替换
-
字符串拼接,一般用于模糊查询中,建议少用,因为有sql注入的风险。
-
里面如何写,也分两种情况:
-
如果是简单类型,则${}里面随便写。
<select id="getByName" parameterType="string" resultType="users"> -->入参是实体类 select id,username from users where username like'%{tzw}%' -->随便写 </select>
-
如果parameterType的类型是实体类类型,则${}里面只能是类中成员变量的名称。(现在很少用)
-
-
优化后的模糊查询(以后都用这种方式):
//parameterType只有实体类参要传入实体类类型,其它类型可以省略 <select id="getByName" parameterType="String" resultType="users"> select id,username from users where username like concat('%',#{name},'%') </select>
-
字符串替换
-
需求:模糊地址或用户名查询
select * from users where username like '%小%'; select * from users where address like '%市%' <--模糊用户名和地址查询--> <--如果参数超过一个,则parameterType不写--> <select id="getByNameOrAddress" resultType="users"> select id,username,address from users where ${name} like concat('%',#{value},'%') </select>
-
-
-
返回主键值
-
在插入语句结束后,返回自增的主键值到入参的users对象的id属性中。
<insert id="insert" parameterType="users"> <selecKey KeyProperty="id" resultType="int" order="AFTER"> select last_insert_id() <--返回最近插入值自增的id值--> </selecKey> insert into users (username,address)values(#{userName},#{address}) </insert>
-
selecKey标签的参数详解:
-
keyProperty:users对象的那个属性来接返回的主键值。
-
resultType:返回的主键的类型。
-
order:在插入语句执行前,还是执行后返回主键的值。(BEFER、AFTER)
-
-
-
UUID:这是一个全球唯一字符串,由36个字母数字加下划线组成。
@Test public void testUUID(){ UUID uuid = UUID.randomUUID(); System.out.println(uuid); }
-
什么是动态sql:
-
可以定义代码片段,可以进行逻辑判断,可以进行循环处理(批量处理),使条件判断更为简单。
-
<sql>:用来定义代码片段,可以将所有的列名或复杂的条件定义为代码片段,供使用时使用。
-
<include>:用来引用<sql>定义的代码片段。
//定义代码片段 <sql id="allColumns"> id,username,birthday,sex,address </sql> //引用定义好的代码片段 <select id="getAll" resultType="users"> select<include refid="allColumns"></include> from users </select>
-
<if>:进行条件判断
-
<where>:进行多条件拼接,在查询,删除,更新中使用
<select id="getByCondition" parameterType="users" resultType="users"> select<include refid="allColumns"></include> from users <where> <if test="userName != null and userName != ''"> and username like concat('%',#{userName},'%') </if> <if test="sex != null and sex != ''"> and sex = #{sex} </if> </where> </select>
-
<set>:有选择的进行更新处理,至少更新一列
<update id="updateBySet" parameterType="users"> update users <set> <if test="userName != null and userName != ''"> username = #{userName}, </if> <if test="sex != null and sex != ''"> and sex = #{sex} </if> </set> where id = #{id} </update>
-
<foreach>:用来进行循环遍历,完成循环条件,批量删除(最常用),批量增加,批量更新。
<select id="getById" resultType="users"> select <include refid="allColumns"></include> from users where id in <foreach collection="array" item="id" separator="," open="("close=")"> #{id} </foreach> </select> <foreach>参数详解: collection:用来指定入参类型,如果是List集合,则为list;如果是Map集合,则为map;如果是数组,则为array。 item:每次循环遍历出来的值或对象。 separator:多个值或对象或语句之间的分隔符。 open:整个循环外面的前括号。 close:z
-
批量删除实现:
//int deleteBatch(Integet[] arr); <delete id="deleteBatch"> delete from users where id in <foreach collection="array" item="id" separator="," open="(" close=")"> #{id} </foreach> </delete>
-
批量增加(用的少)
//int insertBatch(List<Users> list); insert into users(username,age,sex,address) values <foreach collection="list" item="u" separator=","> (#{u.userName},#{u.age},#{u.sex},#{u.address}) </foreach>
-
批量更新
-
注意:要使用批量更新,必须在jdbc.properties属性文件中的url中添加&allowMultiQueries=true.表示允许多行操作。(底层是分号结尾,即同时更新多条语句,但是一条语句里面有多行数据受影响,所以要添加该语句)
-
-
-
指定参数位置
-
入参是map(重点)
-
如果入参超过一个以上,使用map封装查询条件,更有语义,查询条件更加明确。
//入参是map List<User>getByMap(Map mpa); #{birthdayBegin}:就是map中得key <select id="getByMap" resultType="user"> select <include refid="allColumns"></include> from user where birthday between #{birthdayBegin} and # {birthdayEnd} </select> 测试类中 @Test public void testGetByMap() throws ParseException{ Data begin = sf.parse("1999-01-01"); Data end = sf.parse("1999-12-31"); Map map = new HashMap<>(); map.put("birthdayBegin",begin); map.put("birthdayEnd",end); List<User>list = uMapper.getByMap(map); list.forEach(user -> System.out.println(user)); }
-
-
返回值是map
-
如果返回的数据实体类无法包含,可以使用map返回多张表中的若干数据。返回后这些数据之间没有任何关系,就是Object类型。
-
返回的map的key就是列名或别名。
//返回值是map(一行) //Map getReturnMap(Integer id); <select id="getReturnMap" parameterType="int" resultType="map"> select username,address from user where id=#{id} </select> //返回多行的map //List<Map> getMap(); <select id="getMap" resultType="map"> select username,age from user </select>
-
-
如果实体类的成员名称和表名中的名称不一致,查询时可以用别名解决。
-
使用resultMap手工完成映射
//private Integer id; //private String name; //使用resultMap手工完成映射 //typed实体类 <resultMap id="bookmap" type="book"> //主键绑定 <id property="id" column="bookid"></id> -->property是主键的成员变量名,column是表中的主键名 //非主键绑定 <result property="name" column="bookname"></result> --> </resultMap> <select id="getAll" resultMap="bookmap"> select bookid,bookname from book </select>
-
表之间的关联关系
-
一对多关联
-
多对一关联
-
一对一关联
-
-
- 点赞
- 收藏
- 关注作者
评论(0)