Mybatis_初识
mybatis是一个持久层框架,主要功能就是封装了JDBC操作的很多细节,使开发者只需要关注sql语句本身。
体现了ORM(对象关系映射)思想:把数据库表和实体类及实体类的属性对应起来,让我们可以仅操作实体类就实现对数据库表的操作。
1.环境搭建
① 导入依赖
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</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>
② 配置环境(也就是配置数据库的有关信息) SqlMapConfig.xml
<?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>
<!--配置环境-->
<environments default="mysql">
<!--配置mysql环境-->
<environment id="mysql">
<transactionManager type="JDBC"></transactionManager>
<!--配置数据源(连接池)-->
<dataSource type="POOLED">
<!--配置连接数据库的四个基本信息-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatistudy"/>
<property name="username" value="root"/>
<property name="password" value="SUJR20051106."/>
</dataSource>
</environment>
</environments>
<!--指定映射配置文件的位置,映射配置文件指的是每个dao独立的配置文件-->
<mappers>
<mapper resource="此处是dao的配置文件路径"/>
</mappers>
</configuration>
可以看到与之前惯用的JDBC方法相比,不同之处是将连接、配置数据库信息的代码放到了配置文件中,当然这并不是重点,因为之前也学过把信息配置成源数据写在配置文件中。
我认为如何将JDBC中的Statement和ResultSet两部分代替,才是亮点所在。
接下来以一个具体的例子分析如何用Mybatis的方式从数据库查询数据,看看查询语句怎么与数据库连接起来,以及程序如何接收结果。
2. 查询数据演示
① 先去数据库创建一张表,属性及内容如下
② 定义方法,由于我们会在其他地方完成操作,所以用不着写实现类,只写接口定义方法即可
/**
* 用户的持久层接口
*/
public interface IUserDao {
List<User> findAll();
}
② JDBC进行查询操作首先定义查询语句,所以接下来就是以Mybatis的方式定义查询语句
在resources下建立与接口目录级别同名同层次的目录级别,并配置IUserDao.xml映射文件,此xml就是配置sql语句的地方,每个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.stay.dao.IUserDao">
<!--配置findAll()方法 resultType的值是将数据返回到的地方-->
<select id="findAll" resultType="com.stay.domain.User">
select * from user
</select>
</mapper>
③ 定义封装返回值的类——在类里面封装与表中各列同名的属性,由此,表中每行数据都将被封装成一个类的对象。
public class User implements Serializable {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
public Integer getId() {
return id;
}
public void setId(Integer 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 + '\'' +
'}';
}
}
④ 在SqlMapConfig.xml中配置映射文件位置
<!--指定映射配置文件的位置,映射配置文件指的是每个dao独立的配置文件-->
<mappers>
<mapper resource="com/stay/dao/IUserDao.xml"/>
</mappers>
⑤ 运行时先读取配置文件,创建SqlSessionFactory工厂,创建IUserDao接口的代理对象,再用此对象调用findAll()
public static void main(String[] args) throws Exception{
//1.读取配置文件
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创建IUserDao接口的代理对象(因为IUserDao只是个接口所以要为它产生代理对象)
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();
}
输出结果:
至此,用Mybatis的配置完成了一次数据库查询。
也可以用注解实现,只需要修改两个地方的配置即可:
IUserDao.java
public interface IUserDao {
@Select("select * from user") 这是用注解而不用xml文件时的方法
List<User> findAll();
}
SqlSessionConfig.xml
<mappers>
<mapper class="com.stay.dao.IUserDao"/>
</mappers>
3.解析查询步骤
第一步 读取配置文件
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
如果在读取配置文件时使用绝对路径(e:/xxx/xxx.xml)或相对路径(src/java/main/xxx.xml),在不同的情况下都会出现一些问题。绝对路径太过绝对,相对路径在项目部署之后会存在找不到路径的问题。
为了稳妥起见,读取配置文件使用两种方式:
① 使用类加载器,它只能读取类路径的配置文件
② 使用ServletContext对象的getRealPath() 能得到当前应用部署的一个绝对路径,不管项目在哪运行,都能得到一个正确的路径
第二步 创建SqlSessionFactory工厂
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
SqlSessionFactoryBuilder类对象调用build(),根据里面的参数构建好工厂,返回给一个工厂对象的引用。这里使用了构建者模式,把对象的创建细节隐藏,方便使用者拿到对象。
第三步 使用工厂创建SqlSession对象
SqlSession session = factory.openSession();
这里又使用到了工厂模式 ,降低类之间的耦合
第四步 创建Dao接口的代理对象
IUserDao userDao = session.getMapper(IUserDao.class);
IUserDao是个接口,不能实例化,使用session.getMapper()进行代理创建。
备注:CRUD基本操作见工程文件
4.参数深入
4.1 parameterType
4.1.1 传递简单类型
4.1.2 传递 pojo对象(也就是实体类对象)
使用ognl表达式解析对象字段的值,#{}或者${}括号中的值为pojo属性的名称
ognl:通过对象的取值方法来获取数据,在写法上省略了get方法的调用。原因是在parameterType中已经提供了所有属性的类,所以此时不需要再写对象名
4.1.3 传递pojo类包装对象
如果对象的实例是另外一个类的属性,如何传递呢
<select id="findUserByVo" parameterType="com.stay.domain.QueryVo" resultType="com.stay.domain.User">
select * from user where username like #{user.username}
</select>
public class QueryVo {
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
4.2 .返回值深入
4.2.1 resultType
主要解决一个问题就是如果数据库表的列名和实体类的属性名称不一样,数据有可能封装不进去。
解决方法1:在查询语句上改造
select 表中列名1 as 类中属性名1,表中列名2 as 类中属性名2 from 表名
解决方法2:配置表列名和类属性名的对应关系
<!-- resultMap里的id是自定义的-->
<resultMap id="userMap" type="全限定类名">
<!--主键字段-->
< id property="类属性名" column="列名"></id>
<!--非主键字段-->
<result property="类属性名" column="列名"></result>
</resultMap>
在语句里面也要将resultType删掉,换成resultMap ,值为上面配置的resultMap的id值
<select id="findAll" resultMap="userMap">
select * from user
</select>
4.3.属性
4.3.1 properties-resource
可以用来引用外部配置文件信息
<!--可以在标签内部配置连接数据库的信息,也可以通过属性引用外部配置文件信息
resourse:指定配置文件的位置,是按照类路径的写法来写,并且必须存在于类路径下
-->
<properties resource="jdbcConfig.properties">
</properties>
<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>
配置文件:
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatistudy
jdbc.username=root
jdbc.password=12345
4.3.2 properties-url
<!--可以在标签内部配置连接数据库的信息,也可以通过属性引用外部配置文件信息
url:按照url的方式来写地址
-->
<!--指定url的方式-->
<properties url="file:///E:/Web/mybatis_day02_01_mybatisCRUD/src/main/resources/jdbcConfig.properties">
</properties>
file指file协议。
4.3.3 typeAliases
配置别名,只能配置domain中类的别名
<typeAliases>
<!--<typeAlias type="com.stay.domain.User" alias="user"></typeAlias>-->
<!--一个个指定太过麻烦,直接给包指定 指定之后默认类名就是别名,并且不再区分大小写-->
<package name="com.stay.domain"></package>
</typeAliases>
使用示例:
<select id="findAll" resultType="user">
select * from user
</select>
mappers下面也可以用package
<mappers>
<!--<mapper resource="com/stay/dao/IUserDao.xml"/>-->
<!--package标签用于指定dao接口所在的包,当指定了之后就不需要再写mapper以及resource或者class了-->
<package name="com.stay.dao"></package>
</mappers>