Mybatis注解开发
环境搭建
pom.xml
首先引入坐标,我们需要mybatis、mysql、junit这三个必要的坐标,当然,为了方便,这里我还使用了log4j查看日志和lombok处理getter、setter、toString。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>Mybatis04_3</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.5</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</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.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
</dependency>
</dependencies>
</project>
构建项目文件路径
org.example.dao用来存放Dao接口的,org.example.domain存放实体类。
SqlMapConfig.xml
为了方便,之前博客也都有写过,这里使用了引入外部配置文件(jdbcConfig.xml),配置别名,数据库环境,以及引入指定带有注解的dao接口所在位置。
<?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>
<!--引入外部配置文件-->
<properties resource="jdbcConfig.properties"></properties>
<!--配置别名-->
<typeAliases>
<package name="org.example.domain"/>
</typeAliases>
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC"></transactionManager>
<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>
<!--引入指定带有注解的dao接口所在位置-->
<mappers>
<package name="org.example.dao"/>
</mappers>
</configuration>
jdbcConfig.xml
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test?characterEncoding=UTF-8
jdbc.username=root
jdbc.password=root
构建实体类和Dao接口
package org.example.domain;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import java.io.Serializable;
import java.util.Date;
@Setter
@Getter
@ToString
public class User implements Serializable{
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
}
package org.example.dao;
import org.apache.ibatis.annotations.Select;
import org.example.domain.User;
import java.util.List;
/**
* 在mybatis中针对CRUD一共有四个注解
* @Select @Insert @Update @Delete
*/
public interface IUserDao {
//查询所有用户
@Select("select * from user")
List<User> findAll();
}
注:mybatis针对CRUD共有四个注解,分别为@Select、@Insert、@Update、@Delete。走进@Select注解,我们可以发现里面只有一个属性,即value,其实就是我们需要传入的SQL语句,当然此时也不用写value,而是可以直接写SQL语句。
测试
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.example.dao.IUserDao;
import org.example.domain.User;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class mybatisTest1 {
@Test
public void test1() throws IOException {
//1.解析配置文件
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.创建SqlSessionFactory工厂
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//3.创建SqlSession对象
SqlSession sqlSession = factory.openSession();
//4.创建动态代理对象
IUserDao userdao = sqlSession.getMapper(IUserDao.class);
//5.调用方法
List<User> users = userdao.findAll();
for(User u:users){
System.out.println(u);
}
sqlSession.close();
in.close();
}
}
CRUD
增删改
IUserDao
首先在IUserDao中声明修改和更新方法。
//保存用户
@Insert("insert into user(username,address,sex,birthday) values(#{username},#{address},#{sex},#{birthday})")
void saveUser(User user);
//修改操作
@Update("update user set username=#{username},address=#{address},sex=#{sex},birthday=#{birthday} where id=#{id}")
void updateUser(User user);
//删除操作
@Delete("delete from user where id=#{id}")
void deleteUser(Integer id);
测试
为了能更便捷,使用Junit框架中@Before和@After注解,将重复代码抽离出来,封装到init方法(@Before)和destroy方法(@After)中,前者会在测试代码执行前执行,后者会在代码之后执行。
package org.example.test;
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.example.dao.IUserDao;
import org.example.domain.User;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
public class AnnotationTest {
private InputStream in;
private SqlSession sqlSession;
private IUserDao userDao;
@Before
public void init() throws IOException {
in = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
sqlSession = factory.openSession();
userDao = sqlSession.getMapper(IUserDao.class);
}
@After
public void destroy() throws IOException {
sqlSession.commit();
sqlSession.close();
in.close();
}
@Test
public void testUser1(){
User user = new User();
user.setUsername("呜呼呼");
user.setAddress("陕西省西安市");
userDao.saveUser(user);
}
@Test
public void testUser2(){
User user = new User();
user.setId(12);
user.setUsername("乌拉拉");
user.setAddress("山西省太原市");
user.setSex("女");
user.setBirthday(new Date());
userDao.updateUser(user);
}
@Test
public void testUser3(){
userDao.deleteUser(8);
}
}
ResultMap
之前讲xml配置的时候有讲过,如果数据库中命名和对应实体类命名不一致,无法找到对应的关系,注解开发方式当然也有对应的解决策略。这个注解就是Results,下面我们来讲一下它的使用。
如果我们将Java中的User实体类属性名改为如下:
public class User implements Serializable{
private Integer userId;
private String userName;
private Date userBirthday;
private String userSex;
private String userAddress;
}
则对应的Results注解就是如下:
//查询所有用户
@Select("select * from user")
@Results(id="userMap",value = {
@Result(id = true,property = "userId",column = "id"),
@Result(property = "userName",column = "username"),
@Result(property = "userBirthday",column = "birthday"),
@Result(property = "userSex",column = "sex"),
@Result(property = "userAddress",column = "address")
})
List<User> findAll();
此时,我们会发现,如果每个方法上都这么写,那么就很繁杂,所以可以使用ResultMap注解,可以通过刚才Results里的id标识,来找到它。
//根据id查询用户
@Select("select * from user where id=#{id}")
@ResultMap(value={"userMap"})
User findById(Integer userId);
ResultMap是一个数组,可以写多个对应的标识,当然如果只有一个那么也可以直接写@ResultMap(“userMap”)
多表查询
多对一,一多一
多对一,在mybatis中称之为一对一,之前讲xml配置时有账户表(account),一个账户对应一个用户,所以是多对一(一对一)操作,那么我们就完成查询账户表也查询出对应的用户。
Account
实现实体类,因为是一个account对应一个user,所以我们在实体类中添加映射的user属性。
package org.example.domain;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import java.io.Serializable;
@Getter
@Setter
@ToString
public class Account implements Serializable {
private Integer id;
private Integer uid;
private Double money;
//多对一(mybatis中称之为一对一)的映射,一个账户只能属于一个用户
private User user;
}
IAccountDao
接下来,写对应的dao接口,此时要注意,我们需要完成的操作其实可以分为两步:1.先查出账户,2.根据查出的账户表中uid来查找用户。所以可以利用刚才讲的Results注解来引用第二步操作。
IUserDao中的findById操作。
//根据id查询用户
@Select("select * from user where id=#{id}")
@ResultMap(value={"userMap"})
User findById(Integer userId);
IAccountDao
package org.example.dao;
import org.apache.ibatis.annotations.One;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.mapping.FetchType;
import org.example.domain.Account;
import java.util.List;
public interface IAccountDao {
//查询所有账户,并且获取每个账户所属的用户信息
@Select("select * from account")
@Results(id = "accountMap",value = {
@Result(id = true,property = "id",column = "id"),
@Result(property = "uid",column = "uid"),
@Result(property = "money",column = "money"),
@Result(property = "user",column = "uid",one = @One(select = "org.example.dao.IUserDao.findById",fetchType = FetchType.EAGER))
})
List<Account> findAll();
}
多对多,一对多
一个用户可能具有多个账户,现在我们针对查询用户,并显示对应的多个账户(延迟加载)进行操作。首先,User中得包含一对多关系映射,即Account集合。本质和刚才的也一样,不同的是使用的是Many注解,所以还是把它分为两步:1.查询所有用户,2.根据用户表中查询的id,查询对应的账户,并延迟加载。
IAccountDao中添加第二步操作。
@Select("select * from account where uid=#{userId}")
List<Account> findAccountByUid(Integer userId);
IUserDao中完成第一步骤,并标注第二步骤和延迟加载。
//查询所有账户,并且获取每个账户所属的用户信息
@Select("select * from account")
@Results(id = "accountMap",value = {
@Result(id = true,property = "id",column = "id"),
@Result(property = "uid",column = "uid"),
@Result(property = "money",column = "money"),
@Result(property = "user",column = "uid",one = @One(select = "org.example.dao.IUserDao.findById",fetchType = FetchType.EAGER))
})
List<Account> findAll();