文章目录
Mybatis的快速入门
mybatis进行实现的基本步骤为:
-
导入mybatis依赖到pom.xml中
-
创建People表以及对应的People类
public class People { private int id; private String username; private String password; 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 String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Override public String toString() { return "User{" + "id=" + id + ", username='" + username + '\'' + ", password='" + password + '\'' + '}'; } }
-
创建核心配置文件SqlMapper.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="develop"> <environment id="develop"> <transactionManager type="JDBC"></transactionManager> <dataSource type="POOLED"> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/db1?serverTimezone=Asia/Shanghai"/> <property name="username" value="root"/> <property name="password" value="root"/> </dataSource> </environment> </environments> <!--加载外部的映射文件--> <mappers> <mapper resource="mybatis1/demo/mapper/PeopleMapper.xml"></mapper> </mappers> </configuration>
-
创建映射文件PeopleMapper.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org/DTD Mapper 3.0" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="peopleMapper"> <select id="findAll" resultType="mybatis1.demo.domain.People"> select * from people </select> </mapper>
-
开始测试,测试的代码如下所示:
//读取核心配置文件 InputStream resource = Resources.getResourceAsStream("SqlMapper.XML"); //创建Session工厂 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resource); //创建Session会话对象 SqlSession session = sqlSessionFactory.openSession(); //session会话对象调用对应的方法,执行响应的操作,其中方法参数就是映射文件中的namespace + 对应操作的id list<People> peopleList = session.selectList("peopleMapper.findAll"); //释放资源 sqlSession.close();
值得注意的是,如果在运行的时候,提示找不到映射文件,这时候是因为核心配置文件中加载映射文件出现了问题。这时候是因为我们在resources下创建peopleMapper.xml的方式有问题,因为如果我们在resources目录下面,创建mybatis1.demo.mapper目录,然后在这个mapper目录下面创建PeopleMapper.xml文件。尽管在IDEA中看到的是mybatis1.demo.mapper,但是实际上在文件夹上看,PeopleMapper.xml文件位于mybatis1.demo.mapper这个文件夹下面,而不是我们希望的在mybatis1这个目录下的子目录demo的子目录mapper下面.所以我们需要在resources包下面创建包的时候,应该是mybatis1/demo/mapper
,然后这时候文件中才是创建的正确的目录链,然后再在mapper目录下面创建PeopleMapper.xml即可。可以参考下这个资料:MyBatis配置文件找不到Mapper映射文件
当上面运行之后,再次发生报错,提示java.lang.ClassNotFoundException: Cannot find class: com.mysql.cj.jdbc.Driver
,这里我是因为mysql-connector-java的版本太低所导致的,选的是5点多的版本,只要在pom.xml中修改它的版本为8.0.29即可.
所以利用mybatis进行增删改的操作的时候,主要修改映射文件PeopleMapper.xml以及测试代码即可,对应的代码如下所示:
<update id="updateById" parameterType="mybatis1.demo.domain.People">
<!--因为传递的参数是一个对象,那么可以#{username}表示的是参数中的username成员
的值赋值给这个参数-->
update people set username = #{username}, password = #{password} where id = #{id}
</update>
<delete id="deleteById" parameterType="int">
<!--将参数的值赋值给#{a},因为这里传递的参数是一个基本数据类型,所以
花括号里面的值可以任意写,表示参数的名字,#{a}则是获取参数的值-->
delete from people where id = #{a}
</delete>
<insert id="insert" parameterType="mybatis1.demo.domain.People">
insert into people (username,password) values (#{username},#{password})
</insert>
<select id="selectOne" parameterType="Integer" resultType="mybatis1.demo.domain.People">
select * from people where id = #{id}
</select>
<select id="selectCount" resultType="Integer">
select count(*) from people
</select>
测试类代码:
@Test
//插入操作,因为mybatis中执行sql语句之后不是自动提交事务的,所以需要我们手动提交事务
public void test2() throws IOException {
People people = new People();
people.setUserName("TT");
people.setPassword("159753");
//读取核心配置文件
InputStream resource = Resources.getResourceAsStream("SqlMapper.xml");
//创建Session工厂
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resource);
//创建Session会话对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//执行insert方法,从而执行插入操作,方法参数是对应的映射文件中的namespace + 操作的id
sqlSession.insert("peopleMapper.insert",people);
//执行完毕之后,记得手动提交事务
sqlSession.commit();
//释放资源
sqlSession.close();
}
//编辑操作
@Test
public void test3() throws IOException {
People people = new People();
people.setId(8);
people.setUserName("Lucy");
people.setPassword("7777777");
//读取核心配置文件
InputStream resource = Resources.getResourceAsStream("SqlMapper.xml");
//创建session工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resource);
//创建Session会话对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//执行对应的方法,方法参数是对应映射文件的namespace + 对应操作的id
sqlSession.update("peopleMapper.updateById",people);
//执行方法之后,需要手动提交事务
sqlSession.commit();
//释放资源
sqlSession.close();
}
//删除操作
@Test
public void test4() throws IOException {
//读取核心配置文件
InputStream resource = Resources.getResourceAsStream("SqlMapper.xml");
//创建session工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resource);
//创建session会话对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//调用对应的方法,执行响应的操作,其中方法参数是对应的映射文件的namespace + 操作的id
sqlSession.delete("peopleMapper.deleteById",1);
//执行操作之后,需要手动提交事务
sqlSession.commit();
//释放资源
sqlSession.close();
}
@Test
//查询单个对象
public void test5() throws IOException {
//读取核心配置文件
InputStream resource = Resources.getResourceAsStream("SqlMapper.xml");
//创建session工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resource);
//创建session对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//调用对应的方法,方法参数是对应的映射文件namespace + 操作的id
People people = sqlSession.selectOne("peopleMapper.selectOne", 4);
System.out.println(people);
//因为是查询操作,并没有影响到表的状态改变,所以不需要手动提交事务
//释放资源
sqlSession.close();
}
//统计有多少行
@Test
public void test6() throws IOException {
//读取核心配置文件
InputStream resource = Resources.getResourceAsStream("SqlMapper.xml");
//创建session工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resource);
//创建session对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//调用对应的方法,执行对应的操作,方法参数是映射文件中的namespace + 操作的id
int rows = sqlSession.selectOne("peopleMapper.selectCount");
System.out.println(rows);
//释放资源
sqlSession.close();
}
SqlSession中的方法主要有:
方法名 | 作用 |
---|---|
selectList | 查询多个对象,返回值为List<T>,T的类型是由映射文件中对应操作的resultType的值决定的 |
selectOne | 查询单个数据,返回值为T,T的类型是由映射文件中对应操作的的resultType的值决定的 |
update | 进行更新操作 |
delete | 进行删除操作 |
insert | 进行增加操作 |
commit | 提交事务 |
rollback | 回滚事务 |
close | 释放资源 |
getMapper(xxxxMapper.class) | 获取xxxMapper代理对象,用于下面的代理实现dao层操作 |
值得一提的是,因为我们在通过sqSessionFactory对象来获取SqlSession会话对象,调用的是openSession()方法,那么这时候它是默认开启了一个事务,但是这个事务不是自动提交的,所以当执行增删改操作之后,如果没有进行自动提交事务,那么是不会更新操作数据持久化到数据库中,所以我们需要通过sqlSession对象调用commit()方法来提交事务。但是如果sqlSessionFactory调用的是openSession(boolean autoCommited),如果传递的参数为true,那么表示会自动提交事务,否则如果为false,那么和调用openSession()的效果是一样的,不会自动提交事务,当执行增删改操作的时候,需要我们手动提交事务,才会更新操作数据持久化到数据库中。
在映射配置文件中,我们看到resultType的值为mybatis1.demo.domain.People
很长,而且多次出现,那么这时候为了简化书写,我们可以在核心配置文件SqlMapper中利用typeAlias
标签,来给这个值起别名,这样我们只要在映射配置文件中写他的别名即可。对应的typeAlias代码如下所示:
<!--利用typeAslias,来对映射文件中的一些参数的值起别名,例如resultType中的值起别名为people-->
<typeAliases>
<typeAlias type="mybatis1.demo.domain.People" alias="people"/>
</typeAliases>
<!--
<select id="findAll" resultType="mybatis1.demo.domain.People">
因为在核心配置文件SqlMapper.xml中使用了标签typeAlias,给resultType的值
起别名为people,所以下面的resultType=people和当前的resultType="mybatis1.demo.domain.People"
效果等同-->
<select id="findAll" resultType="people">
我们可以在spring-mvc配置文件中,利用context命名空间,来加载外部properties文件来配置数据源,这里我们同样可以在mybatis核心配置文件SqlMapper.xml中利用<properties>
标签,来加载外部的properties文件,如<properties resource="jdbc.prpperties"></properties
表示加载外部的jdbc.properties文件,如果需要获取jdbc.properties文件中的值的时候,同样是通过表达式来获取它的值。对应的代码如下所示:
<!--利用properties标签来加载外部的properties文件-->
<properties resource="jdbc.properties"></properties>
<environments default="develop">
<environment id="develop">
<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>
mybatis的dao层实现
mybatis的dao层实现主要有2中实现方法:
-
手动实现dao层:我们需要定义dao的实现类,然后在对应的service类中通过这个实现类,来实现对应的操作,对应代码为:
PeopleDao代码如下所示:
public interface PeopleDao { List<People> findAll() throws IOException; People selectOne(int id) throws IOException; int selectCount() throws IOException; void updateById(People people) throws IOException; void deleteById(int id) throws IOException; void insert(People people) throws IOException; }
PeopleDaoImpl代码:
public class PeopleDaoImpl implements PeopleDao { @Override public List<People> findAll() throws IOException { //读取mybatis核心配置文件 InputStream resource = Resources.getResourceAsStream("SqlMapper.xml"); //创建sqlsession工厂对象 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resource); //创建session会话对象,调用openSession(boolean autoCommited),从而获取session会话对象, //并且参数为true的时候,是会自动提交事物的 SqlSession sqlSession = sqlSessionFactory.openSession(true); //执行对应的方法,从而执行对应的操作,方法参数是映射文件中的namespace + 操作的id List<People> peopleList = sqlSession.selectList("peopleMapper.findAll"); //释放资源 sqlSession.close(); return peopleList; } @Override public People selectOne(int id) throws IOException { //读取核心配置文件 InputStream resource = Resources.getResourceAsStream("SqlMapper.xml"); //创建工厂对象 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resource); //创建session会话对象 SqlSession sqlSession = sqlSessionFactory.openSession(true);//参数为true,会自动提交事务 //执行对应的操作 People people = sqlSession.selectOne("peopleMapper.selectOne", id); //释放资源 sqlSession.close(); return people; } @Override public int selectCount() throws IOException { //读取核心配置文件 InputStream resource = Resources.getResourceAsStream("SqlMapper.xml"); //创建工厂对象 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resource); //创建会话对象 SqlSession sqlSession = sqlSessionFactory.openSession(true); //执行对应的操作 int count = sqlSession.selectOne("peopleMapper.selectCount"); //释放资源 sqlSession.close(); return count; } @Override public void updateById(People people) throws IOException { //读取核心配置文件 InputStream resource = Resources.getResourceAsStream("SqlMapper.xml"); //创建工厂对象 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resource); //创建会话对象 SqlSession sqlSession = sqlSessionFactory.openSession(true); //执行对应的操作 sqlSession.selectOne("peopleMapper.updateById",people); //释放资源 sqlSession.close(); } @Override public void deleteById(int id) throws IOException { //读取核心配置文件 InputStream resource = Resources.getResourceAsStream("SqlMapper.xml"); //创建工厂对象 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resource); //创建会话对象 SqlSession sqlSession = sqlSessionFactory.openSession(true); //执行对应的操作 sqlSession.selectOne("peopleMapper.deleteById",id); //释放资源 sqlSession.close(); } @Override public void insert(People people) throws IOException { //读取核心配置文件 InputStream resource = Resources.getResourceAsStream("SqlMapper.xml"); //创建工厂对象 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resource); //创建会话对象 SqlSession sqlSession = sqlSessionFactory.openSession(true); //执行对应的操作 sqlSession.selectOne("peopleMapper.insert",people); //释放资源 sqlSession.close(); } }
PeopleService接口以及PeopleServiceImpl:
public interface PeopleService { List<People> findAll() throws IOException; People selectOne(int id) throws IOException; int selectCount() throws IOException; void updateById(People people) throws IOException; void deleteById(int id) throws IOException; void insert(People people) throws IOException; }
public class PeopleServiceImpl implements PeopleService { private PeopleDao peopleDao = new PeopleDaoImpl(); public List<People> findAll() throws IOException { return peopleDao.findAll(); } public People selectOne(int id) throws IOException { return peopleDao.selectOne(id); } public int selectCount() throws IOException { return peopleDao.selectCount(); } public void updateById(People people) throws IOException { peopleDao.updateById(people); } public void deleteById(int id) throws IOException { peopleDao.deleteById(id); } public void insert(People people) throws IOException { peopleDao.insert(people); } }
对应的测试代码:
public class PeopleServiceTest { private PeopleService peopleService = new PeopleServiceImpl(); @Test public void test1() throws IOException { List<People> peopleList = peopleService.findAll(); System.out.println(peopleList); } @Test public void test2() throws IOException { People people = peopleService.selectOne(3); System.out.println(people); } @Test public void test3() throws IOException { int count = peopleService.selectCount(); System.out.println(count); } @Test public void test4() throws IOException { People people = new People(); people.setUserName("小李"); people.setPassword("147852"); peopleService.insert(people); } @Test public void test5() throws IOException { People people = new People(); people.setId(2); people.setUserName("小顾"); people.setPassword("1598632"); peopleService.updateById(people); } @Test public void test6() throws IOException { peopleService.deleteById(3); } }
-
利用代理来实现dao层(重点)
利用代理来实现dao层的时候,我们只需要定义一个接口即可,其中这个接口需要和对应的映射文件保持一致,所以的一致,主要是下面4点要一致:
- 映射文件中的**
namespace
的值和接口的全定名称一致,在右击这个接口,点击copy Reference即可获得全定名称** - 接口中的方法名称要和映射文件中的操作的id要一致
- 接口中的**方法参数类型要和映射文件中对应的操作的方法参数类型(paramType的值)**一致
- 接口的方法返回值类型要和映射文件中对应的操作方法的返回值类型(resultType的值)一致
如下图所示:
然后再测试类中通过session会话对象,调用getMapper,来获取这个Mapper的代理对象,调用相应方法执行相应的操作即可。对应的代码如下所示:
PeopleMapper:
public interface PeopleMapper { List<People> findAll() throws IOException; People selectOne(int id) throws IOException; int selectCount() throws IOException; void updateById(People people) throws IOException; void deleteById(int id) throws IOException; void insert(People people) throws IOException; }
映射文件:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org/DTD Mapper 3.0" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="mybatis1.demo.dao.PeopleMapper"> <!-- <select id="findAll" resultType="mybatis1.demo.domain.People"> 因为在核心配置文件SqlMapper.xml中使用了标签typeAlias,给resultType的值 起别名为people,所以下面的resultType=people和当前的resultType="mybatis1.demo.domain.People" 效果等同--> <select id="findAll" resultType="people"> select * from people </select> <update id="updateById" parameterType="mybatis1.demo.domain.People"> <!--因为传递的参数是一个对象,那么可以#{username}表示的是参数中的username成员 的值赋值给这个参数--> update people set username = #{username}, password = #{password} where id = #{id} </update> <delete id="deleteById" parameterType="int"> <!--将参数的值赋值给#{a},因为这里传递的参数是一个基本数据类型,所以 花括号里面的值可以任意写,表示参数的名字,#{a}则是获取参数的值--> delete from people where id = #{a} </delete> <insert id="insert" parameterType="mybatis1.demo.domain.People"> insert into people (username,password) values (#{username},#{password}) </insert> <select id="selectOne" parameterType="Integer" resultType="mybatis1.demo.domain.People"> select * from people where id = #{id} </select> <select id="selectCount" resultType="Integer"> select count(*) from people </select> </mapper>
核心配置文件SqlMapper.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> <!--利用properties标签来加载外部的properties文件--> <properties resource="jdbc.properties"></properties> <!--利用typeAslias,来对映射文件中的一些参数的值起别名,例如resultType中的值起别名为people--> <typeAliases> <typeAlias type="mybatis1.demo.domain.People" alias="people"/> </typeAliases> <environments default="develop"> <environment id="develop"> <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> <!--加载外部的映射文件--> <mappers> <mapper resource="mybatis1/demo/mapper/PeopleMapper.xml"></mapper> </mappers> </configuration>
测试代码:
public class PeopleProxyTest { @Test public void test1() throws IOException { //读取核心配置文件 InputStream resource = Resources.getResourceAsStream("SqlMapper.xml"); //创建工厂对象 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resource); //获取session会话对象 SqlSession sqlSession = sqlSessionFactory.openSession(); //调用对应的方法,读取PeopleMapper.class,获取代理对象 PeopleMapper mapper = sqlSession.getMapper(PeopleMapper.class); List<People> peopleList = mapper.findAll(); System.out.println(peopleList); //释放资源 sqlSession.close(); } @Test public void test2() throws IOException { //读取核心配置文件 InputStream resource = Resources.getResourceAsStream("SqlMapper.xml"); //创建session工厂对象 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resource); //获取session会话对象 SqlSession sqlSession = sqlSessionFactory.openSession(); //获取代理对象 PeopleMapper mapper = sqlSession.getMapper(PeopleMapper.class); People people = mapper.selectOne(2); System.out.println(people); //释放资源 sqlSession.close(); } @Test public void test3() throws IOException { //读取核心配置文件 InputStream resource = Resources.getResourceAsStream("SqlMapper.xml"); //创建session工厂构造器 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resource); //创建session会话对象 SqlSession sqlSession = sqlSessionFactory.openSession(); //调用getMapper,从而获取代理对象 PeopleMapper mapper = sqlSession.getMapper(PeopleMapper.class); int count = mapper.selectCount(); System.out.println(count); //释放资源 sqlSession.close(); } @Test public void test4() throws IOException { People people = new People(); people.setUserName("小欧"); people.setPassword("1598526"); //读取核心配置文件 InputStream resource = Resources.getResourceAsStream("SqlMapper.xml"); //创建session工厂构造器 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resource); //创建session会话对象 SqlSession sqlSession = sqlSessionFactory.openSession(); //获取代理对象 PeopleMapper mapper = sqlSession.getMapper(PeopleMapper.class); mapper.insert(people); //判断是否成功插入,发现虽然获取session会话对象的时候,没有传递参数true, //但是执行操作,能够将更新操作数据持久化到数据库中了 List<People> peopleList = mapper.findAll(); System.out.println(peopleList); //释放资源 sqlSession.close(); } @Test public void test5() throws IOException { People people = new People(); people.setId(4); people.setUserName("小唐"); people.setPassword("147852"); //读取核心配置文件 InputStream resource = Resources.getResourceAsStream("SqlMapper.xml"); //创建session工厂构造器 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resource); //获取session会话对象 SqlSession sqlSession = sqlSessionFactory.openSession(); //获取代理对象 PeopleMapper mapper = sqlSession.getMapper(PeopleMapper.class); mapper.updateById(people); List<People> peopleList = mapper.findAll(); System.out.println(peopleList); //释放资源 sqlSession.close(); } @Test public void test6() throws IOException { //读取核心配置文件 InputStream resource = Resources.getResourceAsStream("SqlMapper.xml"); //创建session工厂对象 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resource); //创建session会话对象 SqlSession sqlSession = sqlSessionFactory.openSession(); //获取代理对象 PeopleMapper mapper = sqlSession.getMapper(PeopleMapper.class); mapper.deleteById(9); List<People> peopleList = mapper.findAll(); System.out.println(peopleList); //释放资源 sqlSession.close(); } }
- 映射文件中的**
mybatis映射文件的深入学习----动态sql
我们在上面mybatis的快速入门中,映射文件中只涉及到了简单的增删改查的操作,但是如果设计到了相对复杂的查询操作等的时候,是不足以满足我们的需求的,所以需要利用动态的if标签以及循环来实现。
-
if判断
如果设计到多条件的查询的时候,这时候因为条件的个数并不确定,所以这时候的sql语句是动态,那么这时候我们需要通过if标签来判断是否需要将这个条件添加到sql语句中,如果条件为true,那么就将这个条件添加到sql语句中,否则不需要。对应的代码如下所示:
<select id="findByCondition" resultType="people" parameterType="people"> select * from people where 1=1 <if test="id != 0"> and id = #{id} </if> <if test="username != null"> and username = #{username} </if> <if test="password != null"> and password = #{password} </if> </select>
这里需要注意的几点的是:
- where子句后面
1=1
是不可以省略的,因为一旦我们省略了,那么如果没有传递条件的时候,那么这时候就会因为多出一个where,从而导致sql语句存在语法错误。或者虽然存在条件,但是因为第一个条件中存在着一个and
导致sql语句存在着语法错误,所以where子句后面的1=1
是不可以省略的。 - if中的and是不建议写在if标签的外面的,因为同样是考虑到了条件的不确定性,从而导致可能多出and,从而导致sql语句存在语法的错误。所以需要将and写在if标签的里面
- test进行判断的时候,不建议使用
#{xxx}
来获取xxx的值,然后再进行判断,否则就会发生报错,例如上面查询的时候,需要判断id是否为0,如果test是#{id} != 0
这样写的话,就会提示Cause: java.lang.NumberFormatException: For input string: "{0=null}"
。其余同理。所以我们需要直接用它的名称进行判断即可。
而上面的代码中,和下面的代码作用等同的:
<sql id="selectPeople">select * from people</sql> <select id="findByCondition" resultType="people" parameterType="people"> <!-- 这里使用where标签,相当于上面的where 1=1的作用,这样 如果含有条件的话,例如只有1个条件,那么多出来的and就可以和 条件1=1拼接起来,从而不会发生报错,否则,如果没有1=1这个子条件 那么就会因为sql语句的书写而导致错误。同时and不可以写在if的外面, 因为不知道条件是否存在,如果不存在,同样会因为多出来的and导致 sql语句存在语法错误 --> <include refid="selectPeople"></include> <!--利用include标签,来引用refid对应的sql语句--> <where> <!--如果没有存在条件的时候,那么没有添加where子句--> <if test="id != 0"> <!--这里不可以写#{id} != 0,否则就会发生报错,提示{0=null}--> and id = #{id} </if> <if test="username != null"> <!-- 不要忘记写and,否则就会导致如果前面条件存在的话,那么没有使用and拼接 如果前面的条件不存在的话,会自动帮我们清除前面的and,所以建议在第二个 条件开始,里面的if需要加上and --> and username = #{username} </if> <if test="password != null"> and password = #{password} </if> </where> </select>
可以看到上面的代码中使用到了sql标签,来封装多次使用的sql语句中的公共部分,然后如果需要引用这一个sql语句的时候,需要使用include标签即可引用到了对应的sql语句。
同时通过将条件封装到了where标签中,那么如果不存在条件的时候,那么where不会添加到sql语句中,否则,一旦存在条件,那么就会将where添加到sql语句中,并且每一个if中都有写and,并且第一个if中的and会自动帮我们清除的,所以并不会存在因为
and
的原因而导致sql的语法错误。 - where子句后面
-
foreach循环
当我们需要执行的是xxx in (yyy1,yyy2,yyy3)的操作的时候,由于不知道括号里面参数的个数,所以我们需要利用寻来来实现,对应的代码为:
<!--根据id in (xxx,xxx)进行查询操作--> <select id="findByIn" resultType="people" parameterType="list"> select * from people <!--注意的是这里没有where,因为传递的参数中元素个数可能为空的--> <foreach collection="list" open="where id in (" close=")" separator="," item="id"> <!-- collection就是需要遍历的参数类型 open表示如果list不为空,那么就会将它的值拼接到sql语句中 close表示当list遍历结束之后,同样将它的值拼接到sql语句中 seperator则表示遍历得到的元素将会以它的值作为分隔符进行分隔 因为id in (1,2,3,4)是以逗号作为分隔符的,所以它的值应该是逗号 item就是将遍历到的元素--> #{id} </foreach> </select>
-
公共部分的提取以及应用
当映射文件中某个sql语句出现了多次,那么这时候为了降低代码的重复率,并且使得代码更容易维护,我们需要将公共部分利用
<sql id="xxx">公共部分的sql语句</sql>
来提取公共部分的SQL语句,然后利用include标签来引用对应id的sql语句,对应的格式为<include refid="xxx"></include>
。
值得一提的是,如果我们需要再运行的时候,需要将对应的sql语句打印到控制台中,那么需要利用到了日志来实现这一点,所以我们需要导入log4j依赖,然后将log4j.properties添加到resources目录下即可,其中log4j.properties的内容为:
log4j.rootLogger=DEBUG, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=[%t] %-5p %c %x - %m%n
#log4j.appender.R=org.apache.log4j.DailyRollingFileAppender
#log4j.appender.R.File=../logs/service.log
#log4j.appender.R.layout=org.apache.log4j.PatternLayout
#log4j.appender.R.layout.ConversionPattern=[service] %d - %c -%-4r [%t] %-5p %c %x - %m%n
#log4j.logger.com.ibatis = debug
#log4j.logger.com.ibatis.common.jdbc.SimpleDataSource = debug
#log4j.logger.com.ibatis.common.jdbc.ScriptRunner = debug
#log4j.logger.com.ibatis.sqlmap.engine.impl.SqlMapClientDelegate = debug
#log4j.logger.java.sql.Connection = debug
log4j.logger.java.sql.Statement = debug
log4j.logger.java.sql.PreparedStatement = debug
log4j.logger.java.sql.ResultSet =debug
当上面的操作完毕之后,那么这时候我们运行的时候,就会看到测试的sql语句了,如下所示:
mybatis中的核心配置文件的深入学习
在上面说的核心配置文件中sqlMapper.xml中,主要有提到了以下几个标签:
- configurantion:说明这个文件是mybatis的核心配置文件
- properties: 通过这个标签,来加载外部的properties文件,格式为:
<properties resource="xxxxx"/>
- typeAlias:来说明映射文件某些属性值起别名,例如上面中的people,它是mybatis1.demo.domain.People的别名.
- environments:来配置数据源的
- mappers:加载映射文件的
这里还需要说到了mybatis中的类型处理器以及分页处理所使用到的标签.
-
mybatis中的类型处理器,利用typeHandlers标签来实现
在mybatis中,已经存在了一些类型处理器,通过这些类型处理器,能够将java中的某一些类型,自动转换成为sql中的对应类型,例如java中的int,可以自动转成sql中的int类型。但是也存在一些情况下,java中的某一类型,不能够自动转成对应sql表中的对应列的类型,例如java中的Date类型不可以转成sql中的long类型,所以这时候我们需要自定义类型处理器,从而实现我们的需求,其中这个自定义类需要实现TypeHandler接口,或者继承BaseTypeHandler<T>.
所以自定义类型处理器的步骤为:
- 创建自定义的类型转换器DateTypeHandler,使得这个自定义类继承了BaseTypeHander<T>,其中,泛型的类型T应该是希望要转成sql中对应类型的java类型。例如这里希望将java中的Date类型转成sql中的long类型,所以应该泛型T应该是Date
- 重写BaseTypeHandler<T>中的setNonNullParameter以及getNullableResult方法。其中setxxxx方法是将java中的对应类型转成sql中对应的类型,而getxxx则是将结果集中sql中的类型转成java中的类型。
- 在核心配置文件中利用typeHandlers标签来注册自定义的类型处理器
- 开始测试即可
对应的代码为:
/* mybatis类型转换:将java中的相关数据类型和sql中相关类型进行转换 例如将java中的Date类型转成sql中的long类型,之后插入到sql中,然后 获取到sql中的数据之后,需要将对应的long类型转成java中的Date类型 基本步骤为: 1、定义一个类,使得这个类继承了BaseTypeHandler<T>,其中的泛型T就是 java中想要转成的sql的类型,例如这里是Date类型的java类型数据转成 sql中的long类型数据。 2、重写方法setNonNullParameter,getNullableResult,其中 setNonNullParameter方法是将java中的Date类型转成对应的sql中的对应类型 而getNullableResult则是将结果集中对应的列转成对应的java类型 3、在mybatis中里利用标签<typeHandlers>来将这个自定义的类型处理器进行注册 4、测试数据 */ public class DateTypeHandler extends BaseTypeHandler<Date> { @Override public void setNonNullParameter(PreparedStatement preparedStatement, int i, Date date, JdbcType jdbcType) throws SQLException { //下标i就是我们希望将Data转成成为long类型之后,插入到的列的下标 long time = date.getTime(); preparedStatement.setLong(i,time); } @Override public Date getNullableResult(ResultSet resultSet, String s) throws SQLException { //s就是对应的列名,我们希望这一列的数据转成java中的Date类型 long value = resultSet.getLong(s);//首先先获取这列的数据 //将value转成对应的Date类型 return new Date(value); } @Override public Date getNullableResult(ResultSet resultSet, int i) throws SQLException { //i是我们希望转成java中Date类型的那一列的下标 long birthday = resultSet.getLong(i); return new Date(birthday); } @Override public Date getNullableResult(CallableStatement callableStatement, int i) throws SQLException { //i同样是希望转成java中Date类型数据的那一列的下标 long birthday = callableStatement.getLong(i); return new Date(birthday); } }
核心配置文件SqlMapper.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> <!--利用properties标签来加载外部的properties文件--> <properties resource="jdbc.properties"></properties> <!--利用typeAslias,来对映射文件中的一些参数的值起别名,例如resultType中的值起别名为people--> <typeAliases> <typeAlias type="mybatis1.demo.domain.People" alias="people"/> </typeAliases> <!--利用typeHandlers来注册自定义的类型处理器--> <typeHandlers> <typeHandler handler="mybatis1.demo.handlers.DateTypeHandler"/> </typeHandlers> <environments default="develop"> <environment id="develop"> <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> <!--加载外部的映射文件--> <mappers> <mapper resource="mybatis1/demo/mapper/PeopleMapper.xml"></mapper> <mapper resource="mybatis1/demo/mapper/PeopleMapper2.xml"></mapper> </mappers> </configuration>
测试类为(通过代理的方式来实现dao层相关操作):
public class DateTypeHandlerTest { @Test //插入People对象 public void test1() throws IOException { People people = new People(); people.setBirthday(new Date()); people.setUserName("萧萧"); people.setPassword("15985263"); //读取核心配置文件 InputStream resource = Resources.getResourceAsStream("SqlMapper.xml"); //创建工厂对象 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resource); //创建session会话对象 SqlSession sqlSession = sqlSessionFactory.openSession(); //获取代理对象 PeopleMapper mapper = sqlSession.getMapper(PeopleMapper.class); //执行插入操作,因为是通过代理方式,那么即使调用的是openSession()来获取session会话对象,执行操作之后也能够自动提交事务,将更新数据持久化到数据库中,而普通的操作中则是需要自己来手动提交事务 mapper.insert(people); //获取所有的people对象 List<People> peopleList = mapper.findAll(); System.out.println(peopleList); //释放资源 sqlSession.close(); } @Test //查询所有的People对象 public void test2() throws IOException { //读取核心配置文件 InputStream resource = Resources.getResourceAsStream("SqlMapper.xml"); //创建工厂对象 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resource); //创建session会话对象 SqlSession sqlSession = sqlSessionFactory.openSession(); //获取代理对象 PeopleMapper mapper = sqlSession.getMapper(PeopleMapper.class); //获取所有的people对象 List<People> peopleList = mapper.findAll(); System.out.println(peopleList); //释放资源 sqlSession.close(); } }
-
mybatis中的分页处理
我们在web开发中,需要定义一个Bean对象,来表示存放查询到的所有数据的相关信息,例如总记录数,每页的记录数等信息,而在mybatis中,可以通过配置核心配置文件来实现,对应的步骤为:
-
导入pagehelper以及jsqlparser依赖,对应的代码为:
<dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version> 3.5.0</version> </dependency> <dependency> <groupId>com.github.jsqlparser</groupId> <artifactId>jsqlparser</artifactId> <version>0.9.5</version> </dependency>
值得一提的是,这里需要保证pagehelper的版本需要和mybatis的版本要比较接近,否则,相差太大,那么测试的时候可能会发生报错,提示
Error parsing SQL Mapper Configuration. Cause: java.lang.ClassCastException: com.github.pagehelper.PageHelper cannot be cast to org.apache.ibatis.plugin.Interceptor
。 -
在核心配置文件中利用
plugins
标签来配置mybatis插件 -
在测试类中创建pageHelper类,然后调用startPage(xxx,xxx2):表示从第xxx页开始查询,并且每页的记录数为xxx2
-
执行查询操作,那么查询到的数据就是数据中的第xxx页,并且有xxx2条记录
对应的代码为:
核心配置文件
<?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标签来加载外部的properties文件--> <properties resource="jdbc.properties"></properties> <!--利用typeAslias,来对映射文件中的一些参数的值起别名,例如resultType中的值起别名为people--> <typeAliases> <typeAlias type="mybatis1.demo.domain.People" alias="people"/> </typeAliases> <!--利用typeHandlers来注册自定义的类型处理器--> <typeHandlers> <typeHandler handler="mybatis1.demo.handlers.DateTypeHandler"/> </typeHandlers> <!--利用plugins标签来配置mybatis插件--> <plugins> <plugin interceptor="com.github.pagehelper.PageHelper"> <property name="dialect" value="mysql"></property> </plugin> </plugins> <environments default="develop"> <environment id="develop"> <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> <!--加载外部的映射文件--> <mappers> <mapper resource="mybatis1/demo/mapper/PeopleMapper.xml"></mapper> <mapper resource="mybatis1/demo/mapper/PeopleMapper2.xml"></mapper> </mappers> </configuration>
测试类代码:
public class DateTypeHandlerTest { /* mybatis中的分页操作:可以通过mybatis中的插件来操作,对应的步骤为: 1、导入pageHelper以及jsqlparser依赖 2、在核心配置文件中利用<plugins>标签,来标记要使用pageHelper中的PageHelper类 3、开始测试,其中利用PageHelper这个类调用startPage(start,pageSize),来获取 第start页的记录,并且每一页有pageSize条记录,然后执行查询操作即可。 此外,我们还可以获取当前页的记录数,总记录数,总页数的数据。 */ @Test public void test3() throws IOException { //读取核心配置文件 InputStream resource = Resources.getResourceAsStream("SqlMapper.xml"); //创建session工厂构造器 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resource); //创建session会话对象 SqlSession sqlSession = sqlSessionFactory.openSession(); //获取代理对象 PeopleMapper mapper = sqlSession.getMapper(PeopleMapper.class); //利用PageHelper,来设置要获取查询记录中的第几页,并且每页有几行 PageHelper pageHelper = new PageHelper(); pageHelper.startPage(2,3);//获取的是第2页,然后每页3行数据 List<People> peopleList = mapper.findAll(); for(People people : peopleList){ System.out.println(people); } PageInfo<People> pageInfo = new PageInfo<>(peopleList); System.out.println("当前页: " + pageInfo.getPageNum() + ",总页数: " + pageInfo.getPages() + ", 总记录数: " + pageInfo.getTotal() + ", 每页记录数: " + pageInfo.getPageSize()); System.out.println("是否为第一页: " + pageInfo.isIsFirstPage() + ",是否为最后一页: " + pageInfo.isIsLastPage()); System.out.println("上一页页号:" + pageInfo.getPrePage() + ", 下一页页号: " + pageInfo.getNextPage()); //释放资源 sqlSession.close(); } }
其中我们可以通过PageInfo这个类调用对应的方法,来获取分页中的信息,例如上一页的页号,当前的页号等。
所以在web开发中,利用pageHelper实现分页操作的主要代码如下所示:(springmvc + mybatis + sql + jsp),mybatis的代码如上面所示,主要是通过代理来实现dao层的操作的。
spring-mvc.xml:
<?xml version="1.0" encoding="UTF-8"?> <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:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!--组件扫描--> <context:component-scan base-package="mybatis1.demo.controller"></context:component-scan> <!--mvc注解驱动,处理请求映射等问题--> <mvc:annotation-driven/> </beans>
web.xml代码(配置spring mvc的前端控制器):
<?xml version="1.0" encoding="UTF-8" ?> <web-app> <display-name>Archetype Created Web Application</display-name> <!--配置spring的前端控制器--> <servlet> <servlet-name>DispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-mvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>DispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
PeopleController代码:
@Controller //将当前的类添加到spring容器中 public class PeopleController { @RequestMapping("/queryAll") public ModelAndView queryAll() throws IOException { ModelAndView modelAndView = new ModelAndView(); //读取核心配置文件 InputStream resource = Resources.getResourceAsStream("SqlMapper.xml"); //创建session工厂构造器 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resource); //创建session会话对象 SqlSession sqlSession = sqlSessionFactory.openSession(); //获取代理对象 PeopleMapper mapper = sqlSession.getMapper(PeopleMapper.class); //获取pageHelper对象,设置从第0页开始查询,每页2行 PageHelper pageHelper = new PageHelper(); //调用startPage,设置要获取的是第几页,每页有多少行 pageHelper.startPage(1,2); //代理对象执行查询操作 List<People> peopleList = mapper.findAll(); //将查询到的数据保存到对应的界面中 modelAndView.addObject("peopleList",peopleList); PageInfo<People> pageInfo = new PageInfo<>(peopleList); modelAndView.addObject("pageInfo",pageInfo); //设置要跳转的页面 modelAndView.setViewName("list.jsp"); return modelAndView; } @RequestMapping("/queryFromPage") public ModelAndView queryFromPage(int page) throws IOException { People people = new People(); ModelAndView modelAndView = new ModelAndView(); //读取核心配置文件 InputStream resource = Resources.getResourceAsStream("SqlMapper.xml"); //创建session工厂 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resource); //创建session会话对象 SqlSession sqlSession = sqlSessionFactory.openSession(); //创建pageHelper对象 PageHelper pageHelper = new PageHelper(); //从id页开始查询 pageHelper.startPage(page,2); //执行查询操作 PeopleMapper mapper = sqlSession.getMapper(PeopleMapper.class); List<People> peopleList = mapper.findAll(); //将数据保存到modelAndView中 modelAndView.addObject("peopleList",peopleList); PageInfo<People> pageInfo = new PageInfo<>(peopleList); modelAndView.addObject("pageInfo",pageInfo); //设置视图名字 modelAndView.setViewName("list.jsp"); return modelAndView; } }
list.jsp:
<%-- Created by IntelliJ IDEA. User: MACHENIKE Date: 2022/7/17 Time: 22:43 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <html> <head> <title>页面查询数据</title> </head> <body> <table> <thead> <td>姓名</td> <td>密码</td> <td>出生日期</td> </thead> <c:forEach items="${peopleList}" var="people"> <tr> <td>${people.username}</td> <td>${people.password}</td> <td>${people.birthday}</td> </tr> </c:forEach> </table> <div> <!--分页列表--> <a href="${pageContext.request.contextPath}/queryFromPage?page=${pageInfo.firstPage}">首页</a> <c:choose> <c:when test="${pageInfo.pageNum == 1}"> <a>上一页</a> </c:when> <c:otherwise> <a href="${pageContext.request.contextPath}/queryFromPage?page=${pageInfo.pageNum - 1}">上一页</a> </c:otherwise> </c:choose> <!--设置分页列表--> <c:set var="begin" value="${pageInfo.pageNum - 1}"></c:set> <c:set var="end" value="${pageInfo.pageNum + 1}"></c:set> <c:if test="${begin <= 0}"> <c:set var="begin" value="1"></c:set> </c:if> <c:if test="${end > pageInfo.pages}"> <c:set var="end" value="${pageInfo.pages}"></c:set> </c:if> <c:if test="${begin == 1}"> <c:set var="end" value="${begin + 2}"></c:set> </c:if> <c:if test="${end == pageInfo.pages}"> <c:set var="begin" value="${end - 2}"></c:set> </c:if> <c:forEach begin="${begin}" end="${end}" var="i"> <c:choose> <c:when test="${i == pageInfo.pageNum}"> <a>[${i}]</a> </c:when> <c:otherwise> <a href="${pageContext.request.contextPath}/queryFromPage?page=${i}">[${i}]</a> </c:otherwise> </c:choose> </c:forEach> <c:choose> <c:when test="${pageInfo.pageNum == pageInfo.pages}"> <a>下一页</a> </c:when> <c:otherwise> <a href="${pageContext.request.contextPath}/queryFromPage?page=${pageInfo.pageNum + 1}">下一页</a> </c:otherwise> </c:choose> <a href="${pageContext.request.contextPath}/queryFromPage?page=${pageInfo.lastPage}">尾页</a> </div> </body> </html>
index.jsp(在原来web目录下面的index.jsp的基础上修改的):
<%@page pageEncoding="UTF-8" language="java" %> <html> <body> <h2>Hello World!</h2> <a href="${pageContext.request.contextPath}/queryAll">查询所有people</a> </body> </html>
测试结果为:当我们一打开服务器的时候,就会默认来到了index.jsp界面,然后我们就点击查询所有people的超链接,就会看到下面结果:
-
这里需要注意的是,如果我们配置spring的前端控制器之后,一启动服务器,就发生了报错,错误信息为:Caused by: java.io.FileNotFoundException: Could not open ServletContext resource [/WEB-INF/DispatcherServlet-servlet.xml]
,那么需要来到web.xml文件中,查看是否将DispatcherServlet配置正确:
- 是否在dispatcherServlet所在的servlet标签下面添加了<init-param>这个子标签
- 如果添加了,那么添加的信息中的<param-name>是否为contextConfigLocation
- <param-value>的值是否为classpath:xxxxx的形式
如果上面中的任意3步都没有实现,那么需要将这2步都实现了,那么才可以正常运行。同理,如果发生了报错,那么可以按照这个思路进行解决.
所以对应的web.xml代码应该是:
<?xml version="1.0" encoding="UTF-8" ?>
<web-app>
<display-name>Archetype Created Web Application</display-name>
<!--配置spring的前端控制器-->
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name> <!--名字一定是这个-->
<param-value>classpath:spring-mvc.xml</param-value> <!--值得形式是classpath:对应的配置文件的格式-->
</init-param>
<load-on-startup>1</load-on-startup> <!--这个不写的时候,目前运行也是正常的,但是建议要加上去-->
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
mybatis的多表查询
在数据库中,表和表之间,可能是一对一的关系,也可能是一对多,多对多的关系,下面将根据这3中关系进行多表查询,对应的操作是基于xml进行操作的,即存在着映射文件xxxMapper.xml。
-
一对一关系:例如现实生活中购物,从订单的角度来说,每一个订单,只能是由一个用户来下单的,所以这时候订单和用户是一个一对一的关系。所以如果我们需要获取订单,以及下单的人的时候,我们可以通过这样做:
准备工作:
创建Order类以及People类:
public class Order { private int id;//订单id private Date orderTime;//下单时间 private Double total;//订单总价 private People people;//下单用户 public int getId() { return id; } public void setId(int id) { this.id = id; } public Date getOrderTime() { return orderTime; } public void setOrderTime(Date orderTime) { this.orderTime = orderTime; } public Double getTotal() { return total; } public void setTotal(Double total) { this.total = total; } public People getPeople() { return people; } public void setPeople(People people) { this.people = people; } @Override public String toString() { return "Order{" + "id=" + id + ", orderTime=" + orderTime + ", total=" + total + ", people=" + people + '}'; } }
public class People { private int id; private String username; private String password; private Date birthday; List<Order> orders;//获取这个用户的所有订单 public List<Order> getOrders() { return orders; } public void setOrders(List<Order> orders) { this.orders = orders; } public Date getBirthday() { return birthday; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public void setBirthday(Date birthday) { this.birthday = birthday; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Override public String toString() { return "People{" + "id=" + id + ", username='" + username + '\'' + ", password='" + password + '\'' + ", birthday=" + birthday + ", orders=" + orders + '}'; } }
对应的接口:
public interface OrderMapper { List<Order> findAll(); }
执行的sql操作应该是:
select orders.id, orders.orderTime, orders.total, people.username, people.password, people.birthday from orders inner join people on orders.uid = people.id;
这时候数据库查询的数据,返回的数据类型既有Order类的数据,也有People类的数据,那么这时候不可以通过requestType来设置返回的数据类型。所以我们需要自定义返回的数据类型,所以需要使用的是resultMap这个属性即可。
所以对应的映射文件为:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org/DTD Mapper 3.0" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="mybatis1.demo.dao.OrderMapper"> <!--type来说明这个resultMap的类型是一个order类型,因为我们调用findAll返回的是List<Order> 其中这里的order是一个别名,它的值应该是mybatis1.demo.domain.Order,通过在核心配置文件中 通过typeAlias标签来设置别名 --> <resultMap id="orderMap" type="order"> <!--property说明的是order类型中的属性的名字,column表示查询的sql语句中的字段名--> <result property="id" column="id"></result> <result property="orderTime" column="orderTime"></result> <result property="total" column="total"></result> <!--这里说明的是order中的属性成员people,它是一个People类 people.xxx表示的是people中的xxx属性成员 <result property="people.id" column="uid"></result> <result property="people.username" column="username"></result> <result property="people.password" column="password"></result> <result property="people.birthday" column="birthday"></result>--> <association property="people" javaType="people"> <!--利用association标签和上面的代码作用等同,设置order中的属性people的各个属性信息 其中association中的property表示的是order类中的people属性名字,javaType的类型是people, javaType中的people同样是一个别名,表示的是mybatis1.demo.domain.People --> <result property="id" column="uid"></result> <!--这是people类型中的id属性对应查询的字段uid--> <result property="username" column="username"></result> <result property="password" column="password"></result> <result property="birthday" column="birthday"></result> </association> </resultMap> <select id="findAll" resultMap="orderMap"> select orders.*, people.username, people.password, people.birthday from orders inner join people on orders.uid = people.id; </select> </mapper>
在核心配置文件中加载这个映射文件,然后就可以开始测试了。
测试代码如下所示:
public class OrderTest { @Test //获取所有的订单,并且获取下单的用户 public void test1() throws IOException { //读取核心配置文件 InputStream resource = Resources.getResourceAsStream("SqlMapper.xml"); //创建session工厂构造器 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resource); //创建session会话对象 SqlSession sqlSession = sqlSessionFactory.openSession(); //获取代理对象 OrderMapper mapper = sqlSession.getMapper(OrderMapper.class); //获取所有的order List<Order> orderList = mapper.findAll(); for(Order order : orderList){ System.out.println(order); } //释放资源 sqlSession.close(); } }
测试结果:
这里需要注意的是,因为sql中的people表中的birthday以及orders表中的orderTime都是一个long类型的,而在java中的实体类中,则是一个Date类型,所以这时候我们需要进行类型处理,这里可以通过上面mybatis中的核心配置文件的深入学习中发现,可以通过定义一个自定义类,然后继承BaseTypeHandler<Date>,重写它的setNonNullParameter(用于将对应的java类型转成希望的sql对应的类型)以及getNullablerResult方法(对应的sql类型转成希望的java类型),然后在核心配置文件中利用标签
<typeHandlers>
来配置类型处理其即可。 -
一对多关系:同样是购物中,一个用户可能会下多个订单,所以这时候从用户的角度来说,用户和订单是一对多的关系,同样是上面一对一关系中的实体类代码,这时候我们只需要修改PeopleMapper代码即可:
对应的PeopleMapper接口:
public interface PeopleMapper { List<People> findAndOrder(); }
对应的映射文件:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org/DTD Mapper 3.0" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="mybatis1.demo.dao.PeopleMapper"> <resultMap id="peopleMap" type="people"> <result property="id" column="id"></result> <result property="username" column="username"></result> <result property="password" column="password"></result> <result property="birthday" column="birthday"></result> <!--一个用户可能会有多个订单,所以order是一个集合List,因此使用的的是collection 标签来设置people类中属性名为order的属性,其中标签中的property的值标签的是people类中的属性orders 的属性名,ofType表示的是集合中元素的数据类型。因为这时候不是一对一的关系,所以并不可以使用 orders.xxx来设置people中的属性orders中对应元素的属性值--> <collection property="orders" ofType="order"> <result property="id" column="order_id"></result> <result property="orderTime" column="orderTime"></result> <result property="total" column="total"></result> </collection> </resultMap> <select id="findAndOrder" resultMap="peopleMap"> <!--查询每一个用户的所有订单--> select people.*, orders.id as order_id, orders.orderTime, orders.total from people inner join orders on orders.uid = people.id; </select> </mapper>
在核心配置文件SqlMapper.xml中加载这个映射文件之后,开始进行测试,那么对应测试代码为:
public class PeopleProxyTest { //获取用户的所有订单 @Test public void test9() throws IOException { //读取核心配置文件 InputStream resource = Resources.getResourceAsStream("SqlMapper.xml"); //创建工厂对象 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resource); //创建会话对象 SqlSession sqlSession = sqlSessionFactory.openSession(); //获取代理对象 PeopleMapper mapper = sqlSession.getMapper(PeopleMapper.class); List<People> peopleList = mapper.findAndOrder(); for(People people : peopleList){ System.out.println(people); } //释放资源 sqlSession.close(); } }
测试结果(一部分)为:
-
多对多关系:以现实生活中,一个用户可能有多个角色,同样一个角色也可能对应多个用户,那么这时候用户和角色是一个多对多的关系,此时仅仅靠用户表和角色表是不能够实现多对多的,所以还需要一个中间表。下面以测试查询所有用户对应的角色为例(这时候因为是查询用户对应的角色,所以一个用户可能有多个角色,所以是一个一对多的关系):
所以对应的实体类代码为:
public class User { private int id; private String name; private String password; private String email; private String phone; private List<Role> roles; public List<Role> getRoles() { return roles; } public void setRoles(List<Role> roles) { this.roles = roles; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone; } @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + ", password='" + password + '\'' + ", email='" + email + '\'' + ", phone='" + phone + '\'' + ", roles=" + roles + '}'; } }
public class Role { private int id; private String name; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Role{" + "id=" + id + ", name='" + name + '\'' + '}'; } }
对应的接口:
public interface UserMapper { List<User> findAll(); }
对应的映射文件:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org/DTD Mapper 3.0" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="mybatis1.demo.dao.UserMapper"> <resultMap id="userMap" type="user"> <!--因为返回的是List<User>,所以这里resultMap中的type值为user,这个user同样也是一个 别名,需要我们在核心配置文件中利用typeAlias来设置--> <result property="id" column="id"></result> <result property="name" column="name"></result> <result property="password" column="password"></result> <result property="phone" column="phone"></result> <result property="email" column="email"></result> <collection property="roles" ofType="role"> <!--这里的collection表示user中的这个成员是一个集合, property值表示User类中的roles成员,而ofType表示的是roles成员 的元素是一个role,其中这个role也是一个别名 --> <result property="id" column="role_id"></result> <result property="name" column="role_name"></result> </collection> </resultMap> <select id="findAll" resultMap="userMap"> <!--如果某一些用户没有分配到角色,那么这时候如果同样需要将这些没有分配角色的用户 返回,所以使用的是外联结--> select user.*, role.id as role_id, role.name as role_name from (user left join user_role on user.id = user_role.user_id ) left join role on role.id = user_role.role_id; </select> </mapper>
对应的测试代码:
public class UserTest { @Test public void test() throws IOException { //读取核心配置文件 InputStream resource = Resources.getResourceAsStream("SqlMapper.xml"); //创建工厂对象 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resource); //创建session会话对象 SqlSession sqlSession = sqlSessionFactory.openSession(); //获取代理对象 UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> userList = mapper.findAll(); for(User user : userList){ System.out.println(user); } //释放资源 sqlSession.close(); } }
测试结果(一部分):
⭐⭐⭐上面的操作都是利用代理来实现dao层的数据操作,关于mybatis中用代理来实现,可以查看前面的总结即可。
mybatis利用注解开发
如果需要利用注解开发的话,那么这时候不再需要映射文件了,但是同样需要在核心配置文件中加载映射关系,所以我们同样需要使用<mappers>
标签来加载映射关系,不过子标签不再是<mapper resource="xxx"/>
,而是<package name="xxx">
,这样就可以扫描name这个包,如果有使用到了@Select
等注解的类,那么说明这个类的作用等同于一个映射文件。
这时候因为没有了映射文件,所以为了区分要进行的操作,我们使用到的注解主要有:
-
@Select(“sql语句”)
-
@Update(“sql语句”)
-
@Delete(“sql语句”)
-
@Insert(“sql语句”)
-
@Results:作用和使用映射文件的时候,resultMap标签的作用是一样的,用于多表查询的时候,设置返回值类型的各个属性字段。
-
@Result:作用和使用映射文件的时候,result标签的作用是一样的,即
<result property="xxx" column="yyy"/>
,从而设置某一个属性xxx的值,对应的是查询字段为yyy的值. -
@One(select=“xxxx”):根据括号里面的sql语句进行查询的,返回的一个对象。
-
@Many(select=“xxxx”):根据括号里面的sql语句进行查询,返回的多个对象。
⭐⭐⭐但是很多时候,@One,@Many中select的值传递的并不是一个sql语句,而是某一个Maper接口的全定名称.方法名,表示的是要执行这个一个Maper接口中的对应方法,例如代码
@One(select = "mybatis2.demo.mapper.PeopleMapper.findById")
,表示的是要执行PeopleMapper接口中的findById操作。
这些注解主要是用在对应的接口中,这样的话,如果我们通过代理来进行操作时,对应的代理对象执行对应的方法的时候,就会根据这个注解,只要要执行什么样的操作了。通过注解开发来实现多表查询来加深这部分知识的认识:
-
一对一关系:在上面的一对一关系中使用的代码的基础上,使用注解开发:
对应接口代码:
public interface OrderMapper { //利用注解来获取所有的订单,并且需要获取这个订单的下单者 @Select("select * from orders") @Results({ //利用@Results注解,它的作用和resultMap的作用是一样的 //和resultMap中的子标签result的作用一样,是结果集中的属性 @Result(property = "id",column = "id"), @Result(property = "orderTime",column = "orderTime"), @Result(property = "total",column = "total"), /* @Result(property = "people.id",column = "uid"), @Result(property = "people.username",column = "username"), @Result(property = "people.password",column = "password"), @Result(property = "people.birthday",column = "birthday") 下面的代码和这段代码相同,不过如果使用这一段代码的时候,那么这时候就需要 修改sql语句了,因为如果使用这段代码的时候,需要查询字段中含有username,password birthday字段。 */ @Result( //这个属性的是people对象,表示这个订单的下单者 property = "people",//这个是Order类中的people属性成员名字 javaType = People.class, column = "uid", //表示根据上面的sql中查询的字段uid,来执行@One中的sql语句,从而找到这个order对应的用户 one = @One(select = "mybatis2.demo.mapper.PeopleMapper.findById") ) }) List<Order> findAll(); //根据id来查询订单 @Select("select orders.id, " + " orders.orderTime, " + " orders.total, " + " orders.uid, " + " people.username, " + " people.password, " + " people.birthday " + " from orders inner join people " + " on orders.uid = people.id " + " where orders.id = #{id}") @Results({ @Result(property = "id",column = "id"), @Result(property = "orderTime",column = "orderTime"), @Result(property = "total",column = "total"), @Result(property = "people.id",column = "uid"), @Result(property = "people.username",column = "username"), @Result(property = "people.password",column = "password"), @Result(property = "people.birthday",column = "birthday") }) Order findById(int id); //根据用户的id来查询订单 @Select("select * from orders where uid = #{uid}") List<Order> findByUId(int uid); }
对应的核心配置文件为:
<?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标签来加载外部的properties文件--> <properties resource="jdbc.properties"></properties> <!--因为在people中java中的Date类型要转成sql中的long类型,所以需要利用 标签typeHandlers,来配置类型处理器--> <typeHandlers> <typeHandler handler="mybatis2.demo.handlers.DateTypeHandler"/> </typeHandlers> <!--利用environments标签来配置数据源--> <environments default="develop"> <environment id="develop"> <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> <!--加载映射关系,此时mappers中的子标签使用的是package,表示会扫描 这个包下面的所有类,如果使用到了@Select这些注解,说明这个接口是一个 包含映射关系的接口--> <mappers> <package name="mybatis2.demo.mapper"/> </mappers> </configuration>
-
一对多关系:上面的一个用户可能会有多个订单,在上面代码基础上,使用注解开发的代码为:
对应的PeopleMapper接口为:
public interface PeopleMapper { //获取这个用户所对应的所有的订单 @Select("select * from people") @Results({ @Result(property = "id",column="id"), @Result(property = "username",column = "username"), @Result(property = "password",column = "password"), @Result(property = "birthday",column = "birthday"), //因为这时候的orders是一个集合,不适合使用orders.id来设置元素的id @Result( //设置People类中名为为orders的属性成员的值 property = "orders", column = "id", //根据上面select中的sql语句找到的people的id,来执行下面many注解中的sql语句,从而找到这个用户对应的订单 javaType = List.class, //因为这个属性是List<Order>,所以类型是一个集合的类型 many = @Many(select = "mybatis2.demo.mapper.OrderMapper.findByUId") ) }) List<People> findPeopleAndOrders(); }
映射文件和上面一对一关系中使用注解开发的映射文件一样,然后就可以进行测试了。
-
多对多关系:在上面多表查询中的多对多关系(查询所有用户对应的角色)的代码基础上进行修改,对应的代码为:
对应的UserMapper接口为:
public interface UserMapper { //获取所有的用户以及它的身份 @Select("select * from user") @Results({ @Result(property = "id",column = "id"), @Result(property = "name",column = "name"), @Result(property = "email",column = "email"), @Result(property = "phone",column = "phone"), @Result(property = "password",column = "password"), @Result( //因为一个用户可能有多个角色,所以不可以使用roles.xxx来设值role的对应属性 property = "roles",//roles是User类中的属性名为roles的成员 column = "id", //上面的sql语句查询的字段id,来执行下面many注解中对应的select语句,来找到这个用户对应的多个Role javaType = List.class, many = @Many(select = "mybatis2.demo.mapper.RoleMapper.findByUid") ) }) List<User> findAll(); }
对应的RoleMapper接口为:
public interface RoleMapper { //寻找某一个用户id对应的角色 @Select("select " + "role.* " + " from role inner join user_role " + " on role.id = user_role.role_id " + " where user_role.user_id = #{uid}") List<Role> findByUid(int uid); }
单的下单者
@Select(“select * from orders”)
@Results({ //利用@Results注解,它的作用和resultMap的作用是一样的
//和resultMap中的子标签result的作用一样,是结果集中的属性
@Result(property = “id”,column = “id”),
@Result(property = “orderTime”,column = “orderTime”),
@Result(property = “total”,column = “total”),
/*
@Result(property = “people.id”,column = “uid”),
@Result(property = “people.username”,column = “username”),
@Result(property = “people.password”,column = “password”),
@Result(property = “people.birthday”,column = “birthday”)
下面的代码和这段代码相同,不过如果使用这一段代码的时候,那么这时候就需要
修改sql语句了,因为如果使用这段代码的时候,需要查询字段中含有username,password
birthday字段。
*/
@Result(
//这个属性的是people对象,表示这个订单的下单者
property = “people”,//这个是Order类中的people属性成员名字
javaType = People.class,
column = “uid”, //表示根据上面的sql中查询的字段uid,来执行@One中的sql语句,从而找到这个order对应的用户
one = @One(select = “mybatis2.demo.mapper.PeopleMapper.findById”)
)
})
List findAll();
//根据id来查询订单
@Select("select orders.id, " +
" orders.orderTime, " +
" orders.total, " +
" orders.uid, " +
" people.username, " +
" people.password, " +
" people.birthday " +
" from orders inner join people " +
" on orders.uid = people.id " +
" where orders.id = #{id}")
@Results({
@Result(property = "id",column = "id"),
@Result(property = "orderTime",column = "orderTime"),
@Result(property = "total",column = "total"),
@Result(property = "people.id",column = "uid"),
@Result(property = "people.username",column = "username"),
@Result(property = "people.password",column = "password"),
@Result(property = "people.birthday",column = "birthday")
})
Order findById(int id);
//根据用户的id来查询订单
@Select("select * from orders where uid = #{uid}")
List<Order> findByUId(int uid);
}
对应的核心配置文件为:
```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>
<!--利用properties标签来加载外部的properties文件-->
<properties resource="jdbc.properties"></properties>
<!--因为在people中java中的Date类型要转成sql中的long类型,所以需要利用
标签typeHandlers,来配置类型处理器-->
<typeHandlers>
<typeHandler handler="mybatis2.demo.handlers.DateTypeHandler"/>
</typeHandlers>
<!--利用environments标签来配置数据源-->
<environments default="develop">
<environment id="develop">
<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>
<!--加载映射关系,此时mappers中的子标签使用的是package,表示会扫描
这个包下面的所有类,如果使用到了@Select这些注解,说明这个接口是一个
包含映射关系的接口-->
<mappers>
<package name="mybatis2.demo.mapper"/>
</mappers>
</configuration>
-
一对多关系:上面的一个用户可能会有多个订单,在上面代码基础上,使用注解开发的代码为:
对应的PeopleMapper接口为:
public interface PeopleMapper { //获取这个用户所对应的所有的订单 @Select("select * from people") @Results({ @Result(property = "id",column="id"), @Result(property = "username",column = "username"), @Result(property = "password",column = "password"), @Result(property = "birthday",column = "birthday"), //因为这时候的orders是一个集合,不适合使用orders.id来设置元素的id @Result( //设置People类中名为为orders的属性成员的值 property = "orders", column = "id", //根据上面select中的sql语句找到的people的id,来执行下面many注解中的sql语句,从而找到这个用户对应的订单 javaType = List.class, //因为这个属性是List<Order>,所以类型是一个集合的类型 many = @Many(select = "mybatis2.demo.mapper.OrderMapper.findByUId") ) }) List<People> findPeopleAndOrders(); }
映射文件和上面一对一关系中使用注解开发的映射文件一样,然后就可以进行测试了。
-
多对多关系:在上面多表查询中的多对多关系(查询所有用户对应的角色)的代码基础上进行修改,对应的代码为:
对应的UserMapper接口为:
public interface UserMapper { //获取所有的用户以及它的身份 @Select("select * from user") @Results({ @Result(property = "id",column = "id"), @Result(property = "name",column = "name"), @Result(property = "email",column = "email"), @Result(property = "phone",column = "phone"), @Result(property = "password",column = "password"), @Result( //因为一个用户可能有多个角色,所以不可以使用roles.xxx来设值role的对应属性 property = "roles",//roles是User类中的属性名为roles的成员 column = "id", //上面的sql语句查询的字段id,来执行下面many注解中对应的select语句,来找到这个用户对应的多个Role javaType = List.class, many = @Many(select = "mybatis2.demo.mapper.RoleMapper.findByUid") ) }) List<User> findAll(); }
对应的RoleMapper接口为:
public interface RoleMapper { //寻找某一个用户id对应的角色 @Select("select " + "role.* " + " from role inner join user_role " + " on role.id = user_role.role_id " + " where user_role.user_id = #{uid}") List<Role> findByUid(int uid); }