本人小白一枚,欢迎大家一起讨论学习,如有错误,还望大家指教。
简述Mybatis主配置文件中的标签
properties标签
:可以在标签内部配置连接数据库的信息,也可以通过属性引用外部配置文件信息。
typeAliases标签
:我们可以采用自定义别名的方式来进行开发,Mybatis默认自定义了很多别名,例如在前面的演示的resultType以及parameterType,当我们的参数或者返回值是String类型时,我们可以直接写类型名称,也可以写包名.类名的方式,如java.lang.String,如下图是Mybatis默认支持的别名。
mappers标签
:该标签用来注册映射配置文件,这里我要强调一下,不同的注册方式,不一定要接口和映射配置文件的路径和名称要一样。
简述Mybatis连接池与事务
-
连接池
:在Mybatis的主配置文件中,可以通过<dataSource type="数据源类型">
来采用Mybatis自己的连接池技术,当然我们也可以来引用外部的连接池技术。Mybatis自己的数据源分为三类。- POOLED:采用传统的javax.sql.DataSource规范中的连接池,Mybatis中有针对规范的实现,我们一般采用这个数据源。
- UNPOOLED:采用传统的获取连接的方式,虽然也实现Javax.sql.DataSource接口,但是并没有使用池化技术。
- JNDI:采用服务器提供的JNDI技术实现,来获取DataSource对象,不同的服务器所能拿到DataSource是不一样。 注意:如果不是web或者maven的war工程,是不能使用的。如果使用的是tomcat服务器,采用连接池就是dbcp连接池。
-
使用外部连接池
:我们这里演示使用Druid连接池,只需要两步即可。- 1、创建连接池工厂对象(继承PooledDataSourceFactory)。
- 2、修改配置文件,这里的 property配置需要根据你选择的数据库连接池的具体实现做调整, Druid需要改成下面的配置,同时type写上第一步创建类的全限定类名。
public class DruidDataSourceFactory extends PooledDataSourceFactory {
public DruidDataSourceFactory() {
this.dataSource = new DruidDataSource();
}
}
<dataSource type="data.DruidDataSourceFactory" >
<property name="driverClass" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
- 简述Mybatis事务
在JDBC中我们可以通过设置setAutoCommit()方法将手动提交事务的方式改为自动提交事务的方式,true表示为自动提交,false为手动提交。Mybatis框架因为是对JDBC的封装,所以Mybatis框架的事务控制方式本身也是用JDBC的setAutoCommit()方法来控制的。我们可以通过SqlSession对象的commit方法和rollback方法实现事务的提交和回滚,也可以在创建SqlSession对象时,将openSession中的方法设置为true,即session=factory.openSesson(true)
;
Mybatis的动态SQL语句
在我们实际开发中,如果遇到多条件查询,并且条件是不固定的时候,我们怎么写SQL语句呢?其实Mybatis为我们提供了动态SQL语句。我们可以根据实体类的不同取值,使用不同的SQL语句来进行查询。
- 动态SQL之
<if>
标签
多条件查询用户列表,如果username不为空可以根据username进行查询,如果address不为空还要加入address作为查询条件,表结构及表中记录和工程结构如下,entity包下用来存放实体类,mapper包下用来存放接口,test包下用来测试,这里就不对主配置的内容进行介绍了。
在entity包下创建User实体类,并提供get、set、toString方法。
public class User implements Serializable {
private int id;
private String username;
private Date birthday;
private String sex;
private String address;
// get/set等方法自行补齐............
}
在mapper包下创建UserMapper接口,并提供根据用户信息进行多条件查询。
public interface UserMapper {
/**
* 多条件查询用户列表
*/
List<User> findByUser(User user);
}
在resources资源下的mapper下创建UserMapper.xml映射配置文件,并在其添加如下内容。注意,我在主配置文件中将实体类批量注册了别名,所以我这里使用的是别名并批量注册了映射文件,同时在<if>
标签的test属性中写的是对象的属性名,这里也是使用的OGNL表达式,另外也要注意where 1 = 1
的作用。
<mapper namespace="mapper.UserMapper">
<select id="findByUser" parameterType="user" resultType="user">
SELECT * FROM user WHERE 1 = 1
<if test="username != null and username != ''">
AND username LIKE "%"#{username}"%"
</if>
<if test="address != null and address != ''">
AND address LIKE "%"#{address}"%"
</if>
</select>
</mapper>
在test包下创建测试类,用来测试以上的方法。
@Test
public void findByUserTest() {
User user = new User();
user.setUsername("王五");
user.setAddress("北");
List<User> users = mapper.findByUser(user);
users.forEach(item -> System.out.println(item));
}
打印结果
- 动态SQL之
<where>
标签
上面的<if>
标签中,我们需要自己进行where 1=1
条件的拼接,为了不手动拼接,我们可以使用<where>
标签简化开发。只需要修改上面的映射配置文件,修改如下,并再次进行测试。
<select id="findByUser" parameterType="user" resultType="user">
SELECT * FROM user
<where>
<if test="username != null and username != ''">
AND username LIKE "%"#{username}"%"
</if>
<if test="address != null and address != ''">
AND address LIKE "%"#{address}"%"
</if>
</where>
</select>
@Test
public void findByUserTest() {
User user = new User();
user.setAddress("上海");
List<User> users = mapper.findByUser(user);
users.forEach(item -> System.out.println(item));
}
- 动态SQL之
<foreach>
标签
当我们需要传入多个id查询用户信息时用下面两个sql实现:
SELECT * FROM user WHERE username LIKE '%张%' AND (id =10 OR id =89 OR id=16)
SELECT FROM user WHERE username LIKE '%张%' AND id IN(10, 89, 16)
这样我们在进行范围查询时,就要将一个集合中的值,作为参数动态添加进来,这样我们就需要<foreach>
标签来进行操作了。
在entity包下创建QueryVo类,并在其添加以下两个属性。
public class QueryVo {
private List<Integer> ids;
private User user;
// get/set等方法自行补齐............
}
在mapper包下的UserMapper接口中添加如下方法。
/**
* 根据id集合以及用户多条件进行查询
*/
List<User> findByVo(QueryVo vo);
在UserMpper.xml中添加如下内容,因为这里我使用了<sql>
标签,目的是将重复的sql进行抽取,使用时引用<include>
标签即可,这样可以简便开发。<foreach>
标签用于遍历集合,collection
:表示要遍历的集合元素。open:表示语句的开始部分,close
:表示结束部分,item
:表示遍历集合过程中每个元素生成的变量名,sperator
:表示分隔符。
<!--抽取重复sql-->
<sql id="defaultUser">SELECT * FROM user</sql>
<select id="findByVo" parameterType="queryVo" resultType="user">
<include refid="defaultUser"></include>
<where>
<if test="user.username != null and user.username != ''">
AND username LIKE "%"#{user.username}"%"
</if>
<if test="ids != null and ids.size() > 0">
<foreach collection="ids" open=" AND id in (" close=")" item="uid" separator=",">
#{uid}
</foreach>
</if>
</where>
</select>
测试代码及测试结果
@Test
public void findByVoTest() {
User user = new User();
user.setUsername("张");
QueryVo vo = new QueryVo();
vo.setUser(user);
vo.setIds(Arrays.asList(41, 42, 43));
List<User> users = mapper.findByVo(vo);
users.forEach(item -> System.out.println(item));
}
Mybatis多表查询
这里使用简单的用户和账户模型来分析Mybatis多表关系。用户表为user表,即咱们上面使用的那张表,账户表Account表,一个用户(User)可以有多个账户(Account)。具体关系如下:
- 一对一查询
查询所有账户信息,并关联用户信息。注意:因为一个账户信息只能供给某一个用户使用,所以从查询账户信息出发关联查询用户信息为一对一查询,如果从用户信息查询用户下的账户信息则为一对多查询,因为一个用户可以有多个账户。
方式一:
使用resultMap,定义专门的resultMap用于映射一对一的查询结构。在entity下创建Account实体类,并在该实体类下添加一个User类型的属性,用表示该账户是哪个用户的。
public class Account {
private int id;
private int uid;
private double money;
private User user;
// get/set等方法自行补齐............
}
在mapper包下创建一个AccountMapper的接口,并在其中添加一个findAll方法
public interface AccountMapper {
/**
* 查询所有账户信息,并包含对应的用户信息。
* @return
*/
List<Account> findAll();
}
在resources路径下的mapper包下创建AccountMapper.xml映射配置文件,并在其添加如下内容。
<mapper namespace="mapper.AccountMapper">
<resultMap id="accountUserMap" type="account">
<id property="id" column="aid"/>
<result property="uid" column="uid"/>
<result property="money" column="money"/>
<association property="user" javaType="user">
<id property="id" column="id"/>
<result property="username" column="username"/>
<result property="address" column="address"/>
<result property="sex" column="sex"/>
<result property="birthday" column="birthday"/>
</association>
</resultMap>
<select id="findAll" resultMap="accountUserMap">
SELECT a.id as aid, a.uid,a.money, u.* FROM account a LEFT JOIN `user` u ON u.id = a.uid
</select>
</mapper>
添加测试代码,并查看测试结果
@Test
public void findAllTest() {
accountMapper = sqlSession.getMapper(AccountMapper.class);
List<Account> all = accountMapper.findAll();
all.forEach(item -> System.out.println(item));
}
方式二:
定义专门的包装类作为输出类型,其中定义了sql查询结果集所有的字段,此方法较为简单,企业中普遍使用这种方法。
public class AccountUser extends User{
private int aid;
private int uid;
private double money;
// get/set等方法自行补齐............
}
在AccountMapper接口中定义方法
public interface AccountMapper {
List<AccountUser> findAll();
}
在AccountMapper.xml映射文件中如下内容
<select id="findAll" resultType="accountUser">
SELECT a.id as aid, a.uid,a.money, u.* FROM account a LEFT JOIN `user` u ON u.id = a.uid
</select>
添加测试代码,并查看结果
@Test
public void findByAllTest() {
accountMapper = sqlSession.getMapper(AccountMapper.class);
List<AccountUser> all = accountMapper.findAll();
all.forEach(item -> System.out.println(item));
}
- 一对多查询
查询所有用户信息及其对应的额账户信息。用户信息和账户信息为一对多的关系,并且查询过程中如果用户没有账户信息,我们也要将此记录查询出来,这里也就是使用左连接。
在entity包下的User类中添加accounts属性。
public class User {
private int id;
private String username;
private Date birthday;
private String sex;
private String address;
private List<Account> accounts;
// get/set等方法自行补齐............
}
在mapper包下中的UserMapper添加如下方法
/**
* 查询用户信息,并查询对应的账户信息
*/
List<User> findAll();
在UserMapper.xml映射配置文件添加如下内容,collection
表示一对多的关系,preperty
表示关联查询的结果集存储在实体类的哪个属性上,ofType
表示指定关联查询的结果集中的对象类型为什么类型,即List中对象类型,此处可以使用别名,也可以使用全限定名。
<resultMap id="userAccountMap" type="user">
<id column="id" property="id"/>
<result column="address" property="address"/>
<result column="username" property="username"/>
<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="userAccountMap">
SELECT u.*, a.id as aid, a.money, a.uid FROM `user` u LEFT JOIN account a ON u.id = a.uid
</select>
添加测试方法,并查看测试结果
@Test
public void findAllTest() {
accountMapper = sqlSession.getMapper(AccountMapper.class);
List<AccountUser> all = accountMapper.findAll();
all.forEach(item -> System.out.println(item));
}
- 多对多查询
前面我们学习了使用Mybatis实现一对多关系的维护,多对多关系可以看成是双向的一对多关系。这里使用用户与角色的关系模型,即一个用户可以拥有多种角色,一个角色同样可以被多个用户使用。
向数据库中添加角色表以及用户角色的中间表,并向表中添加如下记录。
查询所有角色并关联查询对应的用户信息。在entity包下创建role实体类。
public class Role {
private int roleId;
private String roleName;
private String roleDesc;
private List<User> users;
// get/set等方法自行补齐............
}
在mapper包下创建RoleMapper接口,并添加findAll方法。
public interface RoleMapper {
/**
* 查询所有角色信息,并查询出对应关联的用户信息
*/
List<Role> findAll();
}
在resources资源路径下的mapper包中创建RoleMapper.xml映射配置文件
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="mapper.RoleMapper">
<resultMap id="roleUserMap" type="role">
<id property="roleId" column="rid"/>
<result property="roleName" column="role_name"/>
<result property="roleDesc" column="role_desc"/>
<collection property="users" ofType="user">
<id property="id" column="id"/>
<result property="username" column="username"/>
<result property="birthday" column="birthday"/>
<result property="sex" column="sex"/>
<result property="address" column="address"/>
</collection>
</resultMap>
<select id="findAll" resultMap="roleUserMap">
SELECT r.id as rid, r.role_name, r.role_desc, u.* FROM role r INNER JOIN user_role ur ON ur.rid = r.id INNER JOIN `user` u ON u.id = ur.uid
</select>
</mapper>
编写测试代码并查看测试结果,图片过于长,这里分两张进行展示
@Test
public void findByAllRoleTest() {
roleMapper = sqlSession.getMapper(RoleMapper.class);
List<Role> roles = roleMapper.findAll();
roles.forEach(item -> System.out.println(item));
}
从User出发,我们也可以发现一个用户可以具有多个角色,这样用户到角色的关系也还是一个一对多的关系,这样我们就可以将user与role的多对多关系拆解成一对多的关系来实现。这里就不展示用户到角色的一对多的查询了。