环境搭建
第一步,创建maven工程并导入坐标
<dependencies><!-- 导入坐标 --><dependency><groupId>com.totoro</groupId><artifactId>mybatis_user</artifactId><version>3.5.5</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.21</version></dependency><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.12</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version></dependency><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.5</version><scope>test</scope></dependency><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.5</version><scope>compile</scope></dependency></dependencies>
第二步,创建实体类和dao的接口
public class User implements Serializable {private Integer id;private String username;private Date birthday;private String gender;private String address;// get & set or toString functions}
/** 用户持久层接口* 在 xml 里面写sql语句* */public interface IUserDao {/** 查询所有用户* @return* */List<User> findAll();/** 保存用户* @param user* */void saveUser(User user);/** 更新用户信息* @param user* */void updateUser(User user);/** 根据id删除用户* @param id* */void deleteUser(int id);/** 根据id查找用户* @param id* */User findById(Integer id);/** 模糊查询用户信息* @param username* */List<User> findByName(String username);/** 查询所有用户数* */int findTotal();/** 根据传入的参数查询* @param user 查询的条件:用户名或性别或地址或全部* @return* */List<User> findUserByCondition(User user);/** 用户到角色的多对多关系* @return* */List<User> findUserToRole();}
第三步,创建mybatis的主配置文件 SqlMapConfig.xml
<configuration><!-- 配置别名,只能配置domain中类的别名,当指定了别名就不再区分大小写 --><typeAliases><!-- <typeAlias type="com.totoro.domain.User" alias="user"></typeAlias>--><!-- 用于指定要配置的包,当指定之后,该包内的实体类都会注册别名,并且类名就是别名,不再区分大小写--><package name="com.totoro.domain"/></typeAliases><!-- 配置环境 --><environments default="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/notes?serverTimezone=UTC"></property><property name="username" value="root"></property><property name="password" value="llqqssyymm1506059428"></property></dataSource></environment></environments><!-- 指定映射配置文件的位置 --><mappers><!-- <mapper resource="com/totoro/dao/IUserDao.xml"/>--><!-- 用于指定dao接口所在的包(相当于别名),当指定之后就不再需要写mapper resource class了--><package name="com.totoro.dao"/></mappers><!--注解方式:此处使用class属性指定被注解的dao全限定类名<mappers><mapper class="com.totoro.dao.IUserDao"/></mappers> --></configuration>
第四步,创建映射配置文件 IUserDao.xml
<mapper namespace="com.totoro.dao.IUserDao"><!-- 配置 查询结果的列名和实体类的属性名的对应关系 --><resultMap id="userMap" type="com.totoro.domain.User"><!-- 主键 --><id property="id" column="id"></id><!-- 非主键 --><result property="username" column="username"></result><result property="birthday" column="birthday"></result><result property="gender" column="gender"></result><result property="address" column="address"></result><collection property="roles" ofType="role"><id property="roleId" column="id"></id><result property="roleName" column="role_name"></result><result property="roleDesc" column="role_desc"></result></collection></resultMap><!-- 配置查询所有用户, id为方法名称 --><select id="findAll" resultMap="userMap">select * from user</select><!-- 插入信息 --><insert id="saveUser" parameterType="User"><!-- 配置插入操作后,获取插入数据的id --><selectKey keyProperty="id" resultType="int" order="AFTER">select last_insert_id();</selectKey>insert into user(username, address,gender,birthday)values (#{username},#{address},#{gender},#{birthday})</insert><!-- 更新用户信息 --><update id="updateUser" parameterType="User">update user set username=#{username},address=#{address},birthday=#{birthday},gender=#{gender} where id=#{id}</update><!-- 删除用户 --><delete id="deleteUser" parameterType="int">delete from user where id=#{id}</delete><!-- 查询用户 --><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 username like #{username}</select><select id="findTotal" resultType="integer">select count(id) from user</select><!-- 根据条件查询 --><select id="findUserByCondition" resultMap="userMap" parameterType="user">select * from user<where><if test="username != null">and username = #{username}</if><if test="gender != null">and gender = #{gender}</if></where></select><select id="findUserToRole" resultMap="userMap">select u.*,r.id,r.role_name,r.role_desc from user uLEFT OUTER JOIN user_role ur on u.id = ur.uidLEFT OUTER JOIN role r on r.id=ur.rid</select></mapper>
环境搭建的注意事项:
- 在
mybatis中,它把持久层的操作接口名称和映射文件也叫做Mapper,即IUserDao和IUserMapper是一样的 - 在idea中,创建目录
derectory与创建包package是不一样的,com.totoro.my在创建包时会自动分成3级 mybatis的映射配置文件位置必须和dap接口的包结构一致- 映射配置文件的
mapper标签namespace属性必须是dao接口的全限定类名 - 映射配置文件的操作配置(如
select),其id属性的取值必须是dao接口的方法名
第五步,测试类
- 读取配置文件
- 创建SqlSessionFactory工厂
- 使用工厂生产SqlSession对象
- 使用SqlSession创建Dao接口的代理对象
- 使用代理对象执行方法
- 释放资源
在测试类
test里面,@Before在@Test之前执行,@After在@Test之后执行public class AccountTest {private InputStream in;private SqlSession sqlSession;private IAccountDao iAccountDao;@Before // 在测试方法执行之前执行public void init() throws Exception{// 1. 读取配置文件in= Resources.getResourceAsStream("SqlMapConfig.xml");// 2. 创建SqlSessionFactory工厂SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();SqlSessionFactory factory = builder.build(in);// 3. 使用工厂生产SqlSession对象sqlSession = factory.openSession();// 4. 使用SqlSession创建Dao接口的代理对象iAccountDao = sqlSession.getMapper(IAccountDao.class);}@After // 在测试方法执行之后执行public void destroy() throws Exception{// 提交事务sqlSession.commit();// 6. 释放资源sqlSession.close();in.close();}// 查询所有@Testpublic void testFindAll() throws Exception{List<Account> users = iAccountDao.findAll();for(Account account:users){System.out.println(account);}}}
- 注意事项:需要在映射配置中告知mybatis要封装到那个实体类中;配置方式为制定实体类的全限定类名
- mybatis基于注解的入门:
把IUserDao.xml移除,在dao接口的方法上使用@Select注解,并且指定SQL语句,同时需要在SqlMapConfig.xml中的mapper配置时,使用class属性指定dao接口的全限定类名。
配置properties
- 可以在标签内部配置连接数据库的信息,也可以通过属性引用外部配置文件信息
- resource属性,常用的,用于指定配置文件的位置,按照类路径的写法来写,并且必须存在于类路径下。
- url属性,按照url的写法来写地址
URL:uniform resource locator 统一资源定位符,可以唯一标志一个资源的位置,写法:协议+主机名+端口号+URI。URI:uniform resource identifier 统一资源标志符,在应用中唯一定位一个资源 如,新建一个名为
c3p0.properties的文件###### database configuration information ######c3p0.jdbcUrl=jdbc:mysql://LOCALHOST:3306/status?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=truec3p0.driverClass=com.mysql.cj.jdbc.Driverc3p0.user=rootc3p0.password=llqqssyymm1506059428###### C3P0 configuration information ######c3p0.maxPoolSize=100c3p0.minPoolSize=1c3p0.initialPoolSize=5c3p0.maxIdleTime=1800
在mybaits的配置文件中(或者在mybatis与spring整合的配置文件中)引用:
<!-- 读取c3p0.properties中的数据库配置信息 --><context:property-placeholder location="classpath:properties/c3p0.properties"/><!-- 配置数据源 --><bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"><!-- 数据库驱动 --><property name="driverClass" value="${c3p0.driverClass}"/><!-- 连接数据库的url --><property name="jdbcUrl" value="${c3p0.jdbcUrl}"/><!-- 连接数据库的用户名 --><property name="user" value="${c3p0.user}"/><!-- 连接数据库的密码 --><property name="password" value="${c3p0.password}"/><!-- 初始化连接数 --><property name="initialPoolSize" value="${c3p0.initialPoolSize}"/><!-- 最大连接数 --><property name="maxPoolSize" value="${c3p0.maxPoolSize}"/><!-- 最小连接数 --><property name="minPoolSize" value="${c3p0.minPoolSize}"/><!-- 连接的生存时间 --><property name="maxIdleTime" value="${c3p0.maxIdleTime}"/></bean>
ps一个出现过的问题,在做毕业设计时,我是这样写mybatis的配置的,但是为了与前几个测试项目的数据库分开,我就单独创建了一个数据库,而这个properties文件我却是直接复制过去的,在毕设项目测试时(连接tb_admin查询信息),但是两个数据库都有tb_admin,只不过毕设的数据库的一个字段变了,导致一直出现一个error: Unknown column ‘account’ in ‘where clause’,我忘记了数据库变了,却一直在找这个字段怎么就没有呢,这个数据表删了又建,过了好久才意识到可能是数据库名没改过来
连接池
- 可以减少获取连接时的时间
- mybatis中
- 配置位置,主配置文件SqlMapConfig.xml中的dataSource标签,type属性就是表示采用何种连接池方式
- 有3种方式:
POOLED 采用传统的javax.sql.DataSource规范中的连接池,mybatis中有针对规范的实现
UNPOOLED 没有池的思想
JNDI 采用服务器提供的JNDI技术实现获取DataSource对象,不同服务器获取到的不一样。
mybatis的事务
- 什么是事务
是?? - 事务的四大特性ACID
- 不考虑隔离性会产生的3个问题
- 解决办法:四种隔离级别
多条件查询
<if>标签
<!-- 根据条件查询 --><select id="findUserByCondition" resultMap="userMap" parameterType="user">select * from user where 1=1<if test="username != null">and username = #{username}</if><if test="gender != null">and gender = #{gender}</if></select>
<where>标签
<!-- 根据条件查询 --><select id="findUserByCondition" resultMap="userMap" parameterType="user">select * from user<where><if test="username != null">and username = #{username}</if><if test="gender != null">and gender = #{gender}</if></where></select>
<each>
多表查询
一对多、一对一、多对一、多对多
- 一对一,两个表,通过一个外键连接,
- 在实体类中添加主表实体的引用 ```java // 从表实体应该包含一个主表实体的对象引用 private User user;
public User getUser() { return user; }
1. 定义封装account和user的resultMap,此处的type属性 由于在主配置文件中写了别名,则由原来的`com.totoro.domain.Account`简写为`account````xml<resultMap id="accountMap" 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="id"><id property="id" column="id"></id><result property="username" column="username"></result><result property="birthday" column="birthday"></result><result property="gender" column="gender"></result><result property="address" column="address"></result></association></resultMap>
配置文件,配置查询所有账户与对应的账户信息
<select id="findAllAU" resultMap="accountMap">select u.*,a.id as aid,a.uid,a.money from account a, user u where u.id=a.uid</select>
在接口文件中添加函数
List<Account> findAllAU();在测试文件中编写函数完成功能
- 一对多,左表的一个用户可能对应右表的多个账号
select * from user u LEFT OUTER JOIN account a on u.id = a.uid - 多对多,示例:用户与角色。步骤:
1.建立两张表,用户表,角色表。让用户表和角色表具有多对多的关系,需要使用中间表,中间表中包含各自的主键,在中间表中是外键。
2.建立两个实体类,….,让用户和角色的实体类能体现出多对多的关系,各自包含对方一个集合引用
3.建立两个配置文件
4.实现配置:
当查询用户时,可以同时得到用户所包含的所有角色信息
当查询角色时,可以同时得到角色所赋予的用户信息
5.多对多按照老师写的有一些问题,当一个用户有多个角色时,只能得到一个角色信息;反之也是。
加载与缓存
- Mybatis中的延迟加载,按需加载
- 在四种表关系中:
- 一对多、多对多:通常采用延迟加载
- 多对一、一对一:通常采用立即加载
- 延迟加载有问题
- 在主配置文件中添加:
<settings><setting name="lazyLoadingEnabled" value="true"/> <!-- 开启延迟加载 --><setting name="aggressiveLazyLoading" value="false"/></settings>
- Mybatis中的缓存
- 适用于缓存:经常查询,不经常改变,数据的正确与否对最终结果影响不大的
- 不适用于缓存:经常改变的,数据的正确与否对最终结果影响很大(商品的库存、银行的汇率、股市的牌价)
- 一级缓存:mybatis中SqlSession对象的缓存。当我们执行查询之后,查询结果会同时存入SqlSession为我们提供的一块区域中,该区域的结构是一个Map,当查询同样的数据时,会先去sqlsession中查询是否有,有的话直接拿。
- 一级缓存是SqlSession范围的缓存,当调用SqlSession的修改、添加、删除、commit()、close()等方法是,就会清空一级缓存
- 二级缓存:是Mybatis中SqlSessionFactory对象的缓存,由同一个SqlSessionFactory对象创建的SqlSession共享其缓存。二级缓存中存放的是数据而不是对象。步骤:
一、让mybatis框架支持二级缓存(在主配置文件中配置)
<settings><setting name="cacheEnabled" value="true"/></settings>
二、让当前的映射文件支持二级缓存(在IUserDao.xml中配置) <cache/>
三、让当前的操作支持二级缓存(在select标签中配置) useCache="true"
注解开发
- 一共有四个注解:@Select @Insert @Update @Delete
- 模糊查询有字符串拼接与参数占位符两种
- 当实体类中的名称与数据库的名称不一致时,需在
dao中使用@Results进行映射 - 二级缓存,在
IUserDao.java(接口)里加上@CacheNamespace(blocking=true)
注意
- 若用xml形式写sql语句,则
- xml文件需要与dao的文件名一致
- xml文件的文件头的路径,必须与对应的dao文件的路径一致


