mybatis 持久层框架,内部封装了JDBC,屏蔽了JDBC API底层访问细节,采用ORM(Object Relational Mapping)思想解决了实体和数据库映射的问题,应用程序访问数据库更方便。
1 单表操作
1.1 创建表
创建一个用户表:
--用户表
create table tb_user (
id int auto_increment primary key,
user_name varchar(50) not null,
make_time datetime,
update_time datetime
)ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ;
1.2 引入依赖
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.8</version>
</dependency>
<!--用于类中自动生成get、set、构造方法等-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
<scope>provided</scope>
</dependency>
1.3 创建实体类
//用户类,@Data注解用来自动生成set、get等方法
@Data
public class User {
private int id ;
private String userName ;
private Timestamp makeTime;
private Timestamp updateTime;
}
1.4 添加配置
1.4.1 主配置文件
主配置文件主要用来配置需要加载的外部文件路径(properties)、设置数据类型别名(typeAliases)、配置自定义类型处理器(typeHandlers)、配置第三方的插件来进行功能扩展(plugins)、数据源环境配置(environments)、映射文件配置(mappers)等。
mybatis.xml文件:
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--注意:标签有顺序-->
<!--加载外部的properties文件-->
<properties resource="jdbc.properties"/>
<!--为数据类型设置别名,基本数据类型已为默认设置-->
<typeAliases>
<typeAlias type="com.test.entity.User" alias="user"/>
</typeAliases>
<!--配置第三方的插件来进行功能扩展-->
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
</plugin>
</plugins>
<!--数据源环境配置-->
<environments default="dev">
<!--指定当前环境为dev-->
<environment id="dev">
<!--指定事务管理类型为JDBC,直接使用JDBC的提交和回滚设置,依赖于从数据源得到的连接来管理事务作用域-->
<transactionManager type="JDBC"/>
<!--指定数据源类型为数据库连接池-->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<!--加载映射文件-->
<mappers>
<mapper resource="mapper/UserMapper.xml"/>
</mappers>
</configuration>
1.4.2 映射文件
mapper/UserMapper.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">
<!--namespace为接口类全路径-->
<mapper namespace="com.test.dao.UserDao">
<!--表字段和类字段进行映射-->
<!--user为com.test.entity.User的别名-->
<resultMap id="userMap" type="user">
<result column="id" property="id"/>
<result column="user_name" property="userName"/>
<result column="make_time" property="makeTime"/>
<result column="update_time" property="updateTime"/>
</resultMap>
<!--抽取共同的sql片段-->
<sql id="selectUser">select * from tb_user</sql>
<select id="findAll" resultMap="userMap">
<include refid="selectUser"/>
</select>
<select id="findByUserName" resultMap="userMap">
<include refid="selectUser"/>
<where>
<if test="userName != null">
and user_name=#{userName}
</if>
</where>
</select>
<select id="findByUserNames" resultMap="userMap" parameterType="list">
<include refid="selectUser"/>
<where>
<foreach collection="list" item="userName" open="user_name in (" close=")" separator=",">
#{userName}
</foreach>
</where>
</select>
<select id="findById" resultMap="userMap">
<include refid="selectUser"/>
<where>
<if test="id != null">
id = #{id}
</if>
</where>
</select>
<insert id="addUser" parameterType="user">
insert into tb_user(user_name,make_time) values (#{userName},#{makeTime})
</insert>
<update id="updateUser" parameterType="user">
update tb_user set user_name=#{userName} , update_time=#{updateTime} where id=#{id}
</update>
<delete id="deleteUser" parameterType="int">
delete from tb_user where id=#{id}
</delete>
</mapper>
映射文件说明:
标签 | 属性 | 说明 |
---|---|---|
mapper | namespace | 只有mapper映射文件,不创建相关接口类时,一般设置为mapper文件名,如userMap,与select、update、delete、insert标签中的id组成sql唯一标志,用于sql语句执行。 如:sqlSession.select("userMap.findAll"); |
创建相关接口类,通过jdk动态代理方式,完成sql执行时,设置为相关接口类的全路径,如:com.test.dao.UserDao,上述配置文件采用该种方式。 | ||
resultMap | type | 用于类中属性与表字段进行映射,填写需要映射类的全路径,如果在主配置文件中已配置类别名,则填写别名即可。如:主配置文件中标签typeAlias设置com.test.entity.User的别名为user,则此type可设置为user。 |
select | resultMap | 设置返回值类型为映射后的结果,属性值为resultMap标签中的id值。 |
resultType | 设置返回值类型为非映射后的结果 | |
sql | id | 提取的相同sql的id值,在sql语句中导入标签中使用,作为include标签中refid属性值。 |
foreach | collection | 要遍历的集合元素:array数组,arg0, collection, list |
item | 遍历集合的每个元素,生成的变量名,相当于for(String str:List<String> list) 中的str。 | |
open | 语句的开始部分 | |
close | 语句的结束部分 | |
separator | 每个元素之间的分隔符 |
1.4.3 接口类
public interface UserDao {
List<User> findAll();
List<User> findByUserName(@Param("userName") String userName);
List<User> findByUserNames(List<String> users);
User findById(int id);
void addUser(User user);
void updateUser(User user);
void deleteUser(int id);
}
接口类与映射文件(上述mapper/UserMapper.xml)关系说明:
①接口类的全限定名需与映射文件中的namespace相同;
②接口类中的方法名需与映射文件中的每个statement的id相同;
③接口类中的方法输入参数类型需与映射文件中的每个statement的parameterType的类型相同,@Param注解中的名称与映射文件中接收到的参数名称对应;
④接口类中的方法返回类型需与映射文件中的每个statement的resultType的类型相同
1.4.4 测试
执行方法前获取接口,执行后关闭sqlsession:
//通过代理方式获取接口
public class UserTest {
private UserDao userDao;
private SqlSession sqlSession;
@Before
public void before() throws IOException {
//从类路径下、文件系统或url加载资源文件
InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//openSession()默认开启事务,需要手动提交事务
//openSession(boolean autoCommit)参数为true,则自动提交事务,否则需要手动提交
//SqlSession中有所有执行sql语句、提交或回滚事务和获取映射器实例的方法
sqlSession = sqlSessionFactory.openSession(true);
//jdk动态代理方式获得UserDao接口的实现类对象(目标对象)
userDao = sqlSession.getMapper(UserDao.class);
}
@After
public void after(){
sqlSession.close();
}
}
1.4.4.1 添加数据
@Test
public void testAdd() throws IOException {
for(int i=0 ; i < 10 ; i++) {
User user = new User();
user.setUserName("小草"+i);
user.setMakeTime(new Timestamp(new Date().getTime()));
userDao.addUser(user);
}
}
测试结果:
1.4.4.2 查询全部
@Test
public void testFindAll() throws IOException {
//非代理方式执行查询语句:
//sqlSession.select("userMap.findAll");
//参数为:namespace.id
List<User> list = userDao.findAll();
for(User User:list){
System.out.println(User);
}
}
测试结果:
1.4.4.3 分页查询
@Test
public void testPage() throws IOException {
//从第二页开始,每页2条数据
PageHelper.startPage(2,2);
List<User> list = userDao.findAll();
PageInfo<User> pageInfo = new PageInfo<User>(list);
int pages = pageInfo.getPages();//总页数
System.out.println("总页数:"+pages);
for(User user:list){
System.out.println(user);
}
}
测试结果:
1.4.4.4 更新数据
@Test
public void testUpdate() throws IOException {
User user = new User();
user.setId(2);
user.setUserName("小静");
user.setUpdateTime(new Timestamp(new Date().getTime()));
userDao.updateUser(user);
}
测试结果:
1.4.4.5 根据用户名查询
@Test
public void testFindByUserName() throws IOException {
List<User> list = userDao.findByUserName("小静");
for(User User:list){
System.out.println(User);
}
}
测试结果:
1.4.4.6 删除数据
@Test
public void testDelete() throws IOException {
userDao.deleteUser(2);//删除id为2的数据
}
测试结果:
1.4.5 补充
1.4.5.1 分页查询
需要引入分页插件:
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.3.0</version>
</dependency>
主配置文件mybatis.xml中添加插件配置:
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor/">
</plugins>
1.4.5.2 日志输出
为了实现控制台sql输出,需要引入日志框架,设置日志输出级别等。
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
添加log4j.properties配置文件:
log4j.rootLogger=DEBUG,Console
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=%d [%t] %-5p [%c] - %m%n
2 多表查询
2.1 创建多张表
分析用户、账户、角色、权限之间的关系,创建表,E-R图如下:
根据E-R图,分析可以得出,还需要创建用户角色表tb_user_role,角色权限表tb_role_privilege,用户表同第1章节的tb_user。
--账户表
create table tb_account (
id int auto_increment primary key,
account_name varchar(50) not null,
user_id int not null,
money decimal(19,4),
make_time datetime,
update_time datetime
)ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ;
--角色表
create table tb_role(
id int auto_increment primary key,
role_name varchar(50) not null,
make_time datetime,
update_time datetime
)ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ;
--权限表
create table tb_privilege(
id int auto_increment primary key,
privilege_name varchar(50) not null,
make_time datetime,
update_time datetime
)ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ;
--用户角色表
create table tb_user_role(
id int auto_increment primary key,
role_id int not null,
user_id int not null,
make_time datetime,
update_time datetime
)ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ;
--角色权限表
create table tb_role_privilege(
id int auto_increment primary key,
role_id int not null,
privilege_id int not null,
make_time datetime,
update_time datetime
)ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ;
2.2 创建实体类
//用户类
@Data
public class User {
private int id ;
private String userName ;
private Timestamp makeTime;
private Timestamp updateTime;
private List<Role> roles;
private List<Account> accounts;
}
//账户类
@Data
public class Account {
private int id;
private String accountName;
private BigDecimal money;
private Timestamp makeTime;
private Timestamp updateTime;
private User user;
}
//角色类
@Data
public class Role {
private int id ;
private String roleName ;
private Timestamp makeTime;
private Timestamp updateTime;
private List<Privilege> privilege;
private List<User> users;
}
//权限类
@Data
public class Privilege {
private int id ;
private String privilegeName ;
private Timestamp makeTime;
private Timestamp updateTime;
private List<Role> roles;
}
2.3 一对一查询
一个账户只能属于一个人,查询账户所属的用户信息。
2.3.1 配置文件
主配置文件mybatis.xml中增加别名:
<typeAlias type="com.test.entity.Account" alias="account"/>
映射文件AccountMapper.xml文件中增加:
<!--表字段和类字段进行映射-->
<resultMap id="accountMap" type="account">
<result column="id" property="id"/>
<result column="account_name" property="accountName"/>
<result column="money" property="money"/>
<result column="make_time" property="makeTime"/>
<result column="update_time" property="updateTime"/>
<association property="user" javaType="com.test.entity.User">
<result column="uid" property="id"/>
<result column="user_name" property="userName"/>
<result column="make_time" property="makeTime"/>
<result column="update_time" property="updateTime"/>
</association>
</resultMap>
<!--根据用户名查询账户信息,如果无用户名,则查询全部账户所属的用户信息-->
<select id="selectByUserName" resultMap="accountMap">
select * , u.id as uid from tb_account a,tb_user u
<where>
a.user_id=u.id
<if test="userName != null">
and u.user_name=#{userName}
</if>
</where>
</select>
association标签说明:
标签/属性 | 说明 |
---|---|
association | 描述只能有一个的关系,如一个账户只能属于一个用户。 |
property | 传值为主类中某个属性名,如Account类中的user属性。 |
result标签说明:
属性 | 说明 |
---|---|
column | 传值为查询sql结果中的字段,如果查询结果中涉及多个表相同的字段名,要设置别名,此处填字段别名 |
property | 传值为当前要映射类的属性 |
2.3.2 接口类
接口类中增加相应的接口方法:
List<Account> selectByUserName(@Param("userName") String userName);
2.3.3 测试
@Test
public void testSelectByUserName(){
List<Account> list = accountDao.selectByUserName(null);
for(Account account:list){
System.out.println(account);
}
}
测试结果:
2.4 一对多查询
一个人可以有多个账户,查询某个用户的所有账户信息。
表中数据如下:
2.4.1 配置文件
<!--表字段和类字段进行映射-->
<!--user为com.test.entity.User的别名-->
<resultMap id="userMap" type="user">
<result column="id" property="id"/>
<result column="user_name" property="userName"/>
<result column="make_time" property="makeTime"/>
<result column="update_time" property="updateTime"/>
<collection property="accounts" ofType="com.test.entity.Account">
<result column="aid" property="id"/>
<result column="account_name" property="accountName"/>
<result column="money" property="money"/>
<result column="make_time" property="makeTime"/>
<result column="update_time" property="updateTime"/>
</collection>
</resultMap>
<!--根据用户名进行模糊查询-->
<select id="selectUserAccountByUserName" resultMap="userMap">
select * , a.id as aid from tb_user u
left join tb_account a on a.user_id = u.id
<where>
<if test="userName != null">
and u.user_name like concat('%',#{userName},'%')
</if>
</where>
</select>
2.4.2 接口类
List<User> selectUserAccountByUserName(@Param("userName") String userName);
2.4.3 测试
@Test
public void testSelectUserAccountByUserName(){
List<User> list = userDao.selectUserAccountByUserName("小草");
for(User user:list){
System.err.println(String.format("用户:id %s ,用户名%s ,账户如下:",user.getId(),user.getUserName() ) );
List<Account> accounts = user.getAccounts();
for(Account account:accounts){
System.err.println(account);
}
}
}
测试结果:
2.5 多对多查询
某个用户可以有多个角色,某个角色可以属于多个用户,某个角色可以有多个权限,某个权限可以属于多个用户,查询某个用户下的权限。
各表数据如下:
tb_role_privilege插入数据:
insert into tb_role_privilege(role_id,privilege_id,make_time) values(1,1,CURRENT_TIMESTAMP());
insert into tb_role_privilege(role_id,privilege_id,make_time) values(1,2,CURRENT_TIMESTAMP());
insert into tb_role_privilege(role_id,privilege_id,make_time) values(1,3,CURRENT_TIMESTAMP());
insert into tb_role_privilege(role_id,privilege_id,make_time) values(2,1,CURRENT_TIMESTAMP());
tb_user_role插入数据:
insert into tb_user_role(role_id,user_id,make_time) values(1,1,CURRENT_TIMESTAMP());
insert into tb_user_role(role_id,user_id,make_time) values(1,2,CURRENT_TIMESTAMP());
insert into tb_user_role(role_id,user_id,make_time) values(2,3,CURRENT_TIMESTAMP());
2.5.1 配置文件
<resultMap id="userMap" type="user">
<result column="id" property="id"/>
<result column="user_name" property="userName"/>
<result column="make_time" property="makeTime"/>
<result column="update_time" property="updateTime"/>
<collection property="accounts" ofType="com.test.entity.Account">
<result column="aid" property="id"/>
<result column="account_name" property="accountName"/>
<result column="money" property="money"/>
<result column="make_time" property="makeTime"/>
<result column="update_time" property="updateTime"/>
</collection>
<collection property="roles" ofType="com.test.entity.Role">
<result column="rid" property="id"/>
<result column="role_name" property="accountName"/>
<result column="make_time" property="makeTime"/>
<result column="update_time" property="updateTime"/>
<collection property="privileges" ofType="com.test.entity.Privilege">
<result column="pid" property="id"/>
<result column="privilege_name" property="privilegeName"/>
<result column="make_time" property="makeTime"/>
<result column="update_time" property="updateTime"/>
</collection>
</collection>
</resultMap>
<!--根据用户名,查询用户权限-->
<select id="selectUserPrivilegeByUserName" resultMap="userMap">
select * , p.id as pid, ur.role_id as rid from tb_user u
left join tb_user_role ur on ur.user_id=u.id
inner join tb_role_privilege rp on rp.role_id=ur.role_id
inner join tb_privilege p on p.id=rp.privilege_id
<where>
<if test="userName != null">
and u.user_name like concat('%',#{userName},'%')
</if>
</where>
</select>
collection标签说明:
标签/属性 | 说明 |
---|---|
collection | 描述有多个的关系,如一个用户可以有多个角色,一个角色可以有多个权限 |
ofType | 区别于javaType,传值类中包含集合类型需使用这个属性 |
2.5.2 接口类
List<User> selectUserPrivilegeByUserName(@Param("userName") String userName);
2.5.3 测试
@Test
public void testSelectUserPrivilegeByUserName(){
List<User> list = userDao.selectUserPrivilegeByUserName("小草0");
for(User user:list){
System.err.println(String.format("用户:id %s ,用户名%s ,权限如下:",user.getId(),user.getUserName() ) );
List<Role> roles = user.getRoles();
for(Role role:roles){
List<Privilege> privileges = role.getPrivileges();
for(Privilege privilege:privileges){
System.err.println(privilege);
}
}
}
}
测试结果:
3 spring整合mybatis
spring整合mybatis,其实就是将使用mybatis生成mapper接口实现类对象的工作交由spring容器来完成,事务控制也由spring来完成。
3.1 引入依赖
数据源依赖:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.8</version>
</dependency>
数据库驱动依赖:
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
</dependency>
mybatis依赖:
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.8</version>
</dependency>
spring依赖:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.13</version>
</dependency>
<!--spring-jdbc依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.13</version>
</dependency>
<!--aop织入依赖-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
3.2 配置文件
mybatis主配置文件mybatis.xml中删除environments中数据源相关的配置。
applicationContext.xml中配置如下:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
">
<context:component-scan base-package="com.test"/>
<context:property-placeholder location="classpath:jdbc.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!--mybatis sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:mybatis.xml"/>
</bean>
<!--动态代理方式生成mapper实现类对象-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.test.dao"/>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>
<!--事务相关配置-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="myPointcut" expression="execution(* com.test.service.impl.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="myPointcut"></aop:advisor>
</aop:config>
</beans>
3.3 测试
@Service("userService")
public class UserServiceImp implements UserService {
@Resource
private UserDao userDao;
public void addUser(User user) {
userDao.addUser(user);
int i=1/0;
}
}
@ContextConfiguration("classpath:applicationContext.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class MybatisTest {
@Autowired
private UserService userService;
@Test
public void testAdd(){
User user = new User();
user.setUserName("华盛顿");
user.setMakeTime(new Timestamp(new Date().getTime()));
userService.addUser(user);
}
}
测试结果: