环境搭建
第一步,创建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 u
LEFT OUTER JOIN user_role ur on u.id = ur.uid
LEFT 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();
}
// 查询所有
@Test
public 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=true
c3p0.driverClass=com.mysql.cj.jdbc.Driver
c3p0.user=root
c3p0.password=llqqssyymm1506059428
###### C3P0 configuration information ######
c3p0.maxPoolSize=100
c3p0.minPoolSize=1
c3p0.initialPoolSize=5
c3p0.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文件的路径一致