MyBatis的这个老师比较厉害,直接教你手撕源码,但是这个手撕的过程有点困难,有点晦涩。我连听了两遍还是一脸的懵逼,所以这个地方我们有空再回来听,还是把入门的代码在这里记录一下,不然时间越长忘得越多,到时候就啥都想不起来了。
MyBatis的环境搭建
1.创建Maven工程并导入坐标
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
</dependency>
</dependencies>
2.创建实体类和dao的接口
User.class
package com.itheima.domain;
import java.io.Serializable;
import java.util.Date;
public class User implements Serializable {
private int id;
private String username;
private Date birthday;
private String sex;
private String address;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", birthday=" + birthday +
", sex='" + sex + '\'' +
", address='" + address + '\'' +
'}';
}
}
IUserDao.java
package com.itheima.dao;
import com.itheima.domain.User;
import java.util.List;
public interface IUserDao {
List<User> findAll();
}
3.创建Mybatis的主配置文件sqlMapConfig
<?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">
<!-- mybatis的主配置文件 -->
<configuration>
<!-- 配置环境 -->
<environments default="mysql">
<!-- 配置mysql的环境 -->
<environment id="mysql">
<!-- 配置事务的类型 -->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置数据源(连接池) -->
<dataSource type="POOLED">
<!-- 配置连接数据库的4个基本信息 -->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/eesy"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!-- 指定映射配置文件的位置。所谓映射配置文件指的是每个dao独立的配置文件 -->
<mappers>
<mapper resource="com/itheima/dao/IUserDao.xml"/>
</mappers>
</configuration>
4.创建映射配置文件IUserDao.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">
<mapper namespace="com.itheima.dao.IUserDao">
<!-- 配置查询所有 返回类型resultType就是将表和实体类给对应起来-->
<select id="findAll" resultType="com.itheima.domain.User">
select * from user
</select>
</mapper>
最后把老师提供的文件夹中的一个log4j.prperties给粘到resources中,最后的文件结构如图所示:
需要注意的有以下三点:
1.mabatis的映射配置文件位置必须和dao接口的包结构相同
2.映射配置文件的mapper标签namespace属性的取值必须是dao接口的全限定类名
3.映射配置文件的操作配置(select),id属性的取值必须是dao接口的方法名
4.映射配置文件中的select标签属性resultType要注明查询返回的值要封装到哪个对象中去
只要满足了1、2、3条件就可以不用创建dao实现类了。
环境配置完我们就可以写测试代码了:
public class MybatisTest {
public static void main(String[] args) throws Exception {
//1.读取配置文件
//绝对路径和相对路径在实际开发中用的少
//一般只用两种方法:
//1.使用类加载器,他们能读取类路径的配置文件
//2.只用Servlet对象的getRealPath()
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.创建SqlSessionFactory工厂,使用了构建者模式,优势:把对象的创建细节隐藏,让使用者直接调用方法就可得到对象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
//3.使用工厂生产SqlSession对象,使用了工厂模式,优势:降低类之间的依赖关系
SqlSession session = factory.openSession();
//4.使用SqlSession创建Dao接口的代理对象,使用了代理模式,优势:不修改源码的基础上对已有方法增强
IUserDao userDao = session.getMapper(IUserDao.class);
//5.使用代理对象实现方法
List<User> users = userDao.findAll();
for (User user:users) {
System.out.println(user);
}
//6.释放资源
session.close();
in.close();
}
}
以下是测试结果:
有的人说自己写映射配置文件会很复杂,所以我们可以采用注解的方式来实现,只需要更改IUserDao接口就行了:
package com.itheima.dao;
import com.itheima.domain.User;
import java.util.List;
@select(select * from user)
public interface IUserDao {
List<User> findAll();
}
然后我们需要在主配置类中通过class属性来指定接口的全限定类名就行了:
<mappers>
<mapper class="com.itheima.dao.IUserDao.class"/>
</mappers>
最后说说我们的测试类中使用到的设计模式,一共三种:构造者模式、工厂模式、代理模式
有关于注解开发还有一个比较有意思的事情,那就是当我们的映射配置文件xml还存在的时候,而程序中也用了注解,那么我们的程序就会报错,不管在主配置文件中写的是
<mappers>
<mapper class="com.itheima.dao.IUserDao.class"/>
</mappers>
还是
<mappers>
<package name="com.itheima.dao"></package>
</mappers>
这是mybatis在设计时默认的加载机制有关。现在已经可以实现数据库的CURD的操作,但是有一个问题,那就是我们数据库中表中的每一项的名称必须要和我们实体类中变量的名称是一样的,比如id,你创建的实体类中也必须要有这么一个id变量,改成userId就立马拉胯。所以针对这种情况mybatis给我们提供了一个注解,方便我们将实例类中变量名和表中的列名进行一一映射的关系。使用形式如下:
@Results(id="userMap",value={
@Result(id=true,column="id",property="userId"),
@Result(column="username",property="userName"),
@Result(column="address",property="userAddress"),
@Result(column="sex",property="userSex"),
@Resulr(column="birthday",property="userBirthday"),
})
写在IUserDao中要执行的数据库操作的方法之前,然后我们之后在其他方法上仍然想要使用这组映射,直接写@ResultMap("userMap")就行,还有需要注意的是后面跟着的查询变量由于我们的实体类发生了变化,也需要将原先的变量名改成现在实体类中的变量名。
现在我们已经可以实现单表的查询了,但是要实现多表查询应该如何建立两个表之间的映射关系呢,这是一个问题,比如我们除了user表外还存在一个account表,着两个表通过uid-id进行一一关联,要求查询出账户信息比如钱数等之后还要把与之对应的用户信息也查询出来。这是一对一的关系,我们可以用注解来进行实现。在我们已经写好的IAccountDao中的查询方法上给加上注解:
@Results(id="accountMap",value={
@Result(id="accountMap",value={
@Result(id=true,column="id",property="id"),
@Result(column="uid",property="uid"),
@Result(column="money",property="money"),
@Result(property="user",column="uid",one=@One(select="com.itheima.dao.IUserDao.findById,fetch=FetchType.EAGER"))
})
可以看到这里的加载模式是eager,是立即加载的模式,原先就说过一对一选择立即加载,一对多选择延迟加载。有了这个,一对多也好写了,加入一个用户对应多个账户,那么我们就写成:
@Results(id="userMap",value={
@Result(id=true,column="id",property="userId"),
@Result(column="username",property="userName"),
@Result(column="address",property="userAddress"),
@Result(column="sex",property="userSex"),
@Result(column="birthday",property="userBirthday"),
@Result(column="id",@many(select="com.itheima.dao.IUserDao.findAccountByUid",fetchType=FetchType.LAZY))
})