我的世界java 显示坐标
mybatis
- mybatis概述、环境搭建、入门案例、自定义mybatis框架
- mybatis概述
- 入门案例
- 自定义mybatis
- 配置文件配置jdbc
- mabatis基本使用、单表CRUD操作、参数和返回值、dao的编写、配置细节(标签的使用)
- mybatis自定义和环境搭建+完善自定义Mybatis的注解开发
- 简单的CRUD操作
- 数据字段问题:
- mybatis的连接池、事务控制,深入和多表,多表查询(一对多,一对一,多对多)
- 连接池
- 事务控制
- 动态sql语句
- 多表查询
- 延迟加载
- 缓存
- 注解的方式开发:
mybatis概述、环境搭建、入门案例、自定义mybatis框架
mybatis概述
mybatis是一个优秀的基于 java 的持久层框架,它内部封装了 jdbc,使开发者只需要关注 sql语句本身, 而不需要花费精力去处理加载驱动、创建连接、创建 statement 等繁杂的过程。
mybatis通过xml 或注解的方式将要执行的各种statement配置起来,并通过java对象和statement 中 sql 的动态参数进行映射生成最终执行的 sql 语句,最后由 mybatis 框架执行 sql 并将结果映射为 java 对象并 返回。
采用 ORM (Object Relational Mappging 对象关系映射 把数据库表和实体类及实体类的属性对应起来,可以操作实体类就实现操作数据表)思想解决了实体和数据库映射的问题,对 jdbc进行了封装,屏蔽了 jdbc api 底层访问细节,使我 们不用与 jdbc api 打交道,就可以完成对数据库的持久化操作。
入门案例
SqlMapConfig.xml
IUserDao.xml
注意事项:
在Mybatis中,他把持久层的操作接口名称和映射文件也叫做Mapper
所以 IUserDao 和 IUserMapper 是一样的
包:com.lx.dao 是三级结构
目录:com.lx.dao 是一级结构
当我们遵从了3、4、5点之后,在开发中就无需再写 dao 类
mybatis的入门案例
注意事项:
在映射配置中告知mybatis要封装到哪个实体类中
配置方式:指定实体类的全限定类名123456
mybatis基于注解的入门案例:
把IUserDao.xml移除,在dao接口的方法上使用@Select注解,并指定SQL语句
同时需要在SqlMapConfig.xml中的mapper配置时,使用class属性指定dao接口的全限定类名
注意:
在实际开发中,都是越简便越好,所以都采用不写dao实现类的方式,不管使用XML还是注解配置
但是Mybatis支持写dao的方式
自定义mybatis
比如需要实现查询所有:创建代理对象,实现查询所有
配置文件配置jdbc
<configuration> <properties resource="jdbcConfig.properties"> <!-- 配置properties,可以在标签内部配置连接数据库的信息,也可以通过属性引用外部配置文件的信息 resources属性:指定配置文件的位置,按照类路径的写法来写,并且必须存在于类路径下。 --> <!-- <property name="driver" value="com.mysql.cj.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC"/> <property name="username" value="root"/> <property name="password" value="123"/>--> </properties> <!-- 配置环境 --> <environments default="mysql"> <!-- 配置mysql环境 --> <environment id="mysql"> <!-- 配置事务 --> <transactionManager type="JDBC"></transactionManager> <!-- 配置连接池 --> <dataSource type="POOLED"> <property name="driver" value="${jdbc.driver}"></property> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </dataSource> </environment> </environments>jdbcConfig.properties
jdbc.driver=com.mysql.cj.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC jdbc.username=root jdbc.password=123mabatis基本使用、单表CRUD操作、参数和返回值、dao的编写、配置细节(标签的使用)
mybatis自定义和环境搭建+完善自定义Mybatis的注解开发
简单的CRUD操作
IUserDao.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"> <mapper namespace="com.lx.dao.IUserDao"> <select id="findAll" resultType="com.lx.domain.User"> select * from user; </select> <insert id="saveUser" parameterType="com.lx.domain.User"> <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> <update id="updateUser" parameterType="com.lx.domain.User"> update user set username=#{username},address=#{address},sex=#{sex},birthday=#{birthday} where id =#{id}; </update> <delete id="deleteUser" parameterType="Integer"> delete from user where id=#{id} </delete> <select id="findById" parameterType="Integer" resultType="com.lx.domain.User"> select * from user where id=#{id} </select> <select id="findByName" parameterType="String" resultType="com.lx.domain.User"> <!-- select * from user where like #{String} Statement对象的字符串拼接sql--> select * from user where like '%${value}%'<!-- PrepatedStatement的参数占位符 --> </select> <select id="findTotal" resultType="int"> select count(id) from user; </select> </mapper>IUserDao.java
package com.lx.dao; import com.lx.domain.User; import java.util.List; public interface IUserDao { public List<User> findAll(); void saveUser(User user); void updateUser(User user); void deleteUser(Integer id); User findById(Integer id); List<User> findByName(String username); int findTotal(); }SqlMapConfig.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> <!-- 配置环境 --> <environments default="mysql"> <!-- 配置mysql环境 --> <environment id="mysql"> <!-- 配置事务 --> <transactionManager type="JDBC"></transactionManager> <!-- 配置连接池 --> <dataSource type="POOLED"> <property name="driver" value="com.mysql.cj.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC"/> <property name="username" value="root"/> <property name="password" value="123"/> </dataSource> </environment> </environments> <!-- 指定映射配置文件的位置,映射配置文件指的是每个dao独立的配置文件 --> <mappers> <mapper resource="com/lx/dao/IUserDao.xml"></mapper> </mappers> </configuration>test类
package com.lx.test; import com.lx.dao.IUserDao; import com.lx.domain.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.After; import org.junit.Before; import org.junit.Test; import java.io.InputStream; import java.util.Date; import java.util.List; public class MybatisTest { private InputStream in; private SqlSession session; private IUserDao userDao; @Before public void init() throws Exception{ in= Resources.getResourceAsStream("SqlMapConfig.xml"); SqlSessionFactory factory=new SqlSessionFactoryBuilder().build(in); session=factory.openSession(); userDao=session.getMapper(IUserDao.class); } @After public void destroy()throws Exception{ in.close(); session.close(); } @Test public void testFindAll() { List<User> users= userDao.findAll(); for (User user:users){ System.out.println(user); } } @Test public void testSave() { User user=new User(); user.setUsername("mybatis saveuser"); user.setAddress("mybatis"); user.setSex("n"); user.setBirthday(new Date()); userDao.saveUser(user); //提交事务 session.commit(); } @Test public void testUpdate(){ User user=new User(); user.setUsername("mybatis saveuser"); user.setAddress("mybatis"); user.setSex("n"); user.setId(5); user.setBirthday(new Date()); userDao.updateUser(user); } @Test public void testDelete(){ userDao.deleteUser(6); } @Test public void testFindOne(){ userDao.findById(7); } @Test public void testFindByName(){ //select * from user where like #{String} //List<User> users=userDao.findByName("%王%"); //select * from user where like '%${value}%' List<User> users=userDao.findByName("L"); for (User user:users){ System.out.println(user); } } @Test public void testFindTotal(){ int count=userDao.findTotal(); System.out.println(count); } }数据字段问题:
mysql数据库在win系统下不区分大小写,Linux严格区分大小写
在win下 userName = username,userId != id
如何解决?
想办法进行匹配
从sql层面来解决
select id as userId,username as userName,address as userAddress,sex as userSex, brithday as userBrithday from user也可以采用配置的方式
<!-- 配置 查询结果的列名和实体类的属性名对应关系 --> <resultMap id="userMap" type="com.lx.domain.user"> <!--主键字段对应--> <id property="userId" column="id"></id> <!--非主键字段对应--> <result property="userName" column="username"></result> <result property="userAddress" column="address"></result> <result property="userSex" column="sex"></result> <result property="userBirthday" column="birthday"></result> </resultMap> <!--使用结果类型定义时--> //<!--<select id="findAll" resultType="com.lx.domain.User">--> <select id="findAll" resultMap="userMap"> select * from user; </select> <!--执行效率慢了,开发效率提高-->mybatis的连接池、事务控制,深入和多表,多表查询(一对多,一对一,多对多)
连接池
连接池:
- 可以减少连接的次数,连接池就是一个存储连接的一个容器。其实就是一个集合对象,该集合必须是线程安全的,不能两个线程拿到同一个连接,该集合还必须保证队列的特性:先进先出。
mybatis的连接池:
- mybatis连接词提供了三种方式的配置:
- 配置的位置:
- 主配置文件sqlMapConfig.xml中的dataSource标签,type属性表示采用何种连接池方式。
- type属性的取值:
- POOLED 采用传统的javax.sql.DataSource规范中的连接池,mybatis中有针对规范的实现
- UNPOOLED 采用传统的获取连接的方式,虽然也实现了javax.sql.DataSource的接口,但是并没有实现池的思想。
- JNDI 采用服务器提供的JNDI技术,来获取DataSource对象,不同的服务器所能拿到的DataSource不一样的。如果不是web或者maven的war工程,是不能使用的。tomcat中采用的连接池就是dbcp连接池
从池中获取一个连接,用完归还
事务控制
Mybatis中的事务:
- 什么是事务
- 事务的四大特性ACID
- 原子性
- 一致性
- 隔离性
- 持久性,具体不解释了,数据库原理的内容。
- 不考虑隔离性会产生的三个问题
- 脏读
- 不可重复读
- 虚读
- 四种隔离级别
- 读取未提交内容
- 读取提交内容
- 可重读
- 可串行化
通过sqlSession对象的commit和rollback方法实现事物的提交和回滚
动态sql语句
直接附上代码
<!--IUserDao.xml--> </resultMap> <!-- 抽取重复的sql语句--> <sql id="defaultUser"> select * from user </sql> <select id="findAll" resultMap="userMap"> <include refid="defaultUser"> </include> </select> <select id="findById" parameterType="Integer" resultMap="userMap"> select * from user where id=#{id} </select> <select id="findByName" parameterType="String" resultMap="userMap"> <!-- select * from user where like #{String} Statement对象的字符串拼接sql--> select * from user where like '%${value}%'<!-- PrepatedStatement的参数占位符 --> </select> <!-- 根据queryVo的条件查询用户--> <select id="findByVo" parameterType="com.lx.domain.QueryVo" resultMap="userMap"> <!-- select * from user where like #{String} Statement对象的字符串拼接sql--> select * from user where like #{user.username}<!-- PrepatedStatement的参数占位符 --> </select> <!-- <select id="findUserByCondition" resultMap="userMap" parameterType="user"> select * from user where 1 = 1 <if test="username != null"> and username=#{username} </if> <if test="sex!=null"> and sex=#{sex} </if> </select>--> <select id="findUserByCondition" resultMap="userMap" parameterType="user"> select * from user <where> <if test="username != null"> and username=#{username} </if> <if test="sex!=null"> and sex=#{sex} </if> </where> </select> <!-- 根据queryVo中的 id集合 实现查询用户列表--> <select id="findUserByIds" resultMap="userMap" parameterType="queryvo"> select * from user <where> <if test="ids!=null and ids.size()>0"> <foreach collection="ids" open="and id in(" close=")" item="id" separator=","> #{id} </foreach> </if> </where> </select>测试类
//MybatisTest @Test public void testFindByCondition(){ User user=new User(); user.setUsername("江安河"); user.setSex("男"); List<User> users=userDao.findUserByCondition(user); for (User userr:users){ System.out.println(userr); } } /** * 测试foreach标签的使用 */ @Test public void testFindInIds(){ QueryVo vo=new QueryVo(); List<Integer> list=new ArrayList<Integer>(); list.add(1); list.add(2); list.add(3); list.add(4); vo.setIds(list); List<User> users=userDao.findUserByIds(vo); for (User userr:users){ System.out.println(userr); } }User类、
package com.lx.domain; import java.io.Serializable; import java.util.Date; public class User implements Serializable { private Integer id; private String username; private String address; private String sex; private Date birthday; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public Date getBirthday() { return birthday; } @Override public String toString() { return "User{" + "id=" + id + ", username='" + username + '\'' + ", address='" + address + '\'' + ", sex='" + sex + '\'' + ", birthday=" + birthday + '}'; } public void setBirthday(Date birthday) { this.birthday = birthday; } }QueryVo
package com.lx.domain; public class QueryVo { private User user; public User getUser() { return user; } public void setUser(User user) { this.user = user; } }多表查询
为什么需要多表查询?
- 比如一个用户有多个订单,一个订单可能有多个商品,一对多
- 多个订单是一个用户下的,今天十个订单,其中三个是同一个人下的,显示就是3-1,1-1,1-1……,多对一
实现如下:
一张USER表,内容为:TEL_NUMBER,USERNAME,SEX,ID之类的
也就是一个手机号,这个人交啥,什么性别,分配了个ID。
需要一个中间表,比如一张表,内容为:TEL_NUMBER,QQ
如何理解呢?手机号唯一,缺点了同一个人,也可以是身份证号之类的唯一标识。这个人有多个QQ号(也可以是游戏角色之类的)。
还需要一张QQ号表,内容为:QQ,SEVER,LEVEL。
如何理解呢?一个QQ号,在一个游戏区里,有一个等级为?的角色
如果要查询一个人他所有的游戏角色,就只需要调用多表查询,由TEL_NUMBER来通过中间表查出他的所有游戏角色表。
为什么不把所有数据写道一张表里呢?会导致一张表非常的笨重臃肿,
代码实现如下
SELECT * FROM QQ号表 qq LEFT OUTER JOIN 中间表 uq ON qq.TEL_NUMBER = uq.TEL_NUMBER LEFT OUTER JOIN 用户表 u ON u.id=uq.uid输出基本靠吼
延迟加载
延迟加载:
- 在真正使用数据时才发起查询,不用的时候不查询,按需加载、懒加载。
对应的,也有立即加载。
比如一个憨憨,创建了一千个游戏角色,他一查QQ,你要不要把一千个游戏角色给他看?还是,他要看的时候再给他看。
好处:节约资源
需要在SqlMapConfig.xml配置,配置在中
QQ号Dao.xml也需要进行相关配置
<association property="" column="" javaType="" select="第二次调用查询的方法的全限定类名+“.”+方法名"> </association>对原有的select语句进行拆分,以前是外连接,现在是查询两次
<select id="findAll" resultMap="userAccountMap"> SELECT * FROM USER u LEFT OUTER JOIN account a ON u.id=a.uid </select> <select id="findById" parameterType="Integer" resultType="user"> select * from user where id=#{id} </select>效果如下、
- 需要查询所有数据的时候
- 不需要查询所有数据的时候
可以看到,当把Account输入信息给注释的时候,并不会发生第二次查询
缓存
Mybatis中有一级缓存和二级缓存,缓存就是存在内存里的东西,拿的快,占内存资源。 目的是减少和数据库的交互次数。理论上内存够大的话,是可以把整个数据库缓存进去的。
有的数据不适用于缓存,比如经常改变却很重要的。比如股票价格。
一级缓存:
- 对 SqlSession 对象的缓存。
- 执行查询之后,查询结果会同时存入到SqlSession提供的一块区域中该区域是一个Map,当我们再次查询同样的数据,mybats会先去sqlsession中查询是否有,有的话直接用
- 当SqlSession对象消失时,mybatis的一级缓存也消失
- 当调用SqlSession的修改,添加,删除,commit(),close()等方法时,就会清空一级缓存
二级缓存:
- 它指的是Mybatis中SqlSessionFactory对象的缓存。由同一个SqlSessionFactory对象创
建的SqlSession共享其缓存。 - 二级缓存的使用步骤:
- 第一步:让Mybatis框架支持二级缓存(在SqlMapConfig.xml中配置)
- 第二步:让当前的映射文件支持二级缓存(在IUserDao.xml中配置)
- 第三步:让当前的操作支持二级缓存(在select标签中配置)
注解的方式开发:
代码一看便知
package com.lx.dao; import com.lx.domain.User; import org.apache.ibatis.annotations.*; import org.apache.ibatis.mapping.FetchType; import java.util.List; /** * mybatis中,CRUD有四个注解 * @Select @Insert @Update @Delete */ @CacheNamespace(blocking = true) public interface IUserDao { @Select(value = "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 = "birthday",property = "userBirthday"), @Result(column = "sex",property = "userSex"), @Result(column = "id",property = "accounts", many = @Many(select = "com.lx.dao.IAccountDao.findAccountByUid", fetchType = FetchType.LAZY)) }) List<User> findAll(); @Select("select * from user where id=#{id}") @ResultMap(value = {"userMap"}) User findById(Integer userId); //@Select("select * from user where username like #{username}") @Select("select * from user where username like '%${value}%' ") @ResultMap("userMap") List<User> findUserByName(String name); }方法的测试和以前相同
io流读取配置文件——SqlSessionFactory——工厂生产SqlSession——session获得dao——dao调用方法
给位,一起冲鸭