一,入门程序
需求:
1、根据用户id查询用户信息
开发步骤:
1.建立工程
2.导入所需jar包
3.创建po
4.创建SqlMapConfig.xml 随便取 今天我们放在config下
5.创建User.xml sql语句都定义在这文件下
6.在 SqlMapConfig.xml 文件中配置 User.xml
7.测试
8.创建log4j.properties
2、根据用户名称模糊查询用户列表
只需要编写
5.创建User.xml sql语句都定义在这文件下
7.测试
3、添加用户
只需要编写
5.创建User.xml sql语句都定义在这文件下
7.测试
示例1:根据用户id查询用户信息
1,建立一个webproject工程
2,导入所需jar包
3,创建po
所谓po可以看成是与数据库中的表相映射的 Java 对象。
创建po即为创建于数据库中表对应的java类
数据库中的表:
对应的java对象
类中的属性也与数据库中属性对应
4.创建SqlMapConfig.xml 随便取 今天我们放在config下
创建configde 的source folder
在configde创建SqlMapConfig.xml的File
<?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>
<!-- mybatis环境变量配置信息 -->
<environments default="development">
<environment id="development">
<!-- jdbc事务管理控制,这里由mybatis管理 -->
<transactionManager type="JDBC"/>
<!-- 数据源的配置 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!-- 映射文件管理 -->
<mappers>
<mapper resource="com/xl/dao/User.xml"/>
</mappers>
</configuration>
5.创建User.xml sql语句都定义在这文件下
<?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命名空间,取名为user -->
<mapper namespace="user">
<!-- 1.根据id查询用户信息
select:相当于statement对象
id:statement唯一标识,调用时使用 必需代表唯一
parameterType:输入映射类型,输入Java类型
resultType:输出映射类型 ,输出java类型 ,本例中输出的为User对象
#{}:相当于占位符
-->
<select id="FindUserById" parameterType="int" resultType="com.xl.po.User">
SELECT * FROM t_user where tid=#{value}
</select>
</mapper>
6.在 SqlMapConfig.xml 文件中配置 User.xml
已在5中完成
7.测试
package test;
import java.io.IOException;
import java.io.InputStream;
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 com.xl.po.User;
public class UserTest {
public static void main(String[] args) throws IOException {
UserTest userTest=new UserTest();
userTest.testFindUserById();
}
public void testFindUserById() throws IOException{
//得到SqlMapConfig.xml
InputStream is=Resources.getResourceAsStream("SqlMapConfig.xml");
//创建SqlSessionFactory
SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(is);
//通过SqlSessionFactory创建SqlSession
SqlSession sqlsession=sqlSessionFactory.openSession();
//执行语句
User user=sqlsession.selectOne("user.FindUserById", 1001);
System.out.println(user);
//释放资源
sqlsession.close();
}
}
8.创建log4j.properties
# Global logging configuration
log4j.rootLogger=DEBUG, stdout
# MyBatis logging configuration...
log4j.logger.org.mybatis.example.BlogMapper=TRACE
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
结果:
**示例2:根据用户名称模糊查询用户列表 **
重写第五步,第七步,其他照旧
创建User.xml sql语句
<!-- 2.根据用户名称模糊查询用户列表 -->
<select id="FindUserByName" parameterType="java.lang.String" resultType="com.xl.po.User">
SELECT * FROM t_user where uname like concat(concat('%',#{value}),'%');
</select>
测试:
public void testFindUserByName() throws IOException{
//得到SqlMapConfig.xml
InputStream is=Resources.getResourceAsStream("SqlMapConfig.xml");
//创建SqlSessionFactory
SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(is);
//通过SqlSessionFactory创建SqlSession
SqlSession sqlsession=sqlSessionFactory.openSession();
//执行语句
List<User> userlist=sqlsession.selectList("user.FindUserById", "二");
System.out.println(userlist);
//释放资源
sqlsession.close();
}
}
**示例3:添加用户 **
在User.xml添加
<!--3.添加用户 -->
<insert id="insertUser" parameterType="com.luban.po.User">
INSERT INTO t_user(tid,uname,birthday,sex,address)
VALUES(t_user_seq.nextval,#{uname},#{birthday},#{sex},#{address})
</insert>
插入用户
public void testInsertUser() throws IOException{
//得到SqlMapConfig.xml
InputStream is=Resources.getResourceAsStream("SqlMapConfig.xml");
//创建SqlSessionFactory
SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(is);
//通过SqlSessionFactory创建SqlSession
SqlSession sqlsession=sqlSessionFactory.openSession();
//创建User对象
User user=new User();
user.setUname("uname1");
user.setSex('1');
user.setAddress("山海");
user.setBirthday(new Date());
//执行语句
int i=sqlsession.insert("user.insertUser", user);
//提交事务
sqlsession.commit();
if(i>0){
System.out.println("输出成功");
}
//释放资源
sqlsession.close();
}
小结
1,parameterType和resultType
parameterType指定输入参数的java类型,可以填写别名或java类的全限定名.
2,#{}和${}
#{}:相当于预处理中的占位符?。
#{}里面的参数表示接收java输入参数的名称。
#{}可以接受HashMap、简单类型、POJO类型的参数。
当接受简单类型的参数时,#{}里面可以是value,也可以是其他。
#{}可以防止SQL注入。
${}:相当于拼接SQL串,对传入的值不做任何解释的原样输出。
${}会引起SQL注入,所以要谨慎使用。
${}可以接受HashMap、简单类型、POJO类型的参数。
当接受简单类型的参数时,${}里面只能是value。
3,selecxtOne和selectList
selecxtOne:只能查询0或1条记录,大于1条记录的话,则报错
selectList:可以查询0或N条记录
二,mybatis开发dao
mybatis在项目中主要使用的地方就是开发dao(数据访问层 ),所以下面讲解一下一步mybatis开发dao的方法。有两种方式
1,原始dao开发方式(ibatis遗留下来的)
2.Mapper代理发发方式(推荐)
1,SqlSession使用范围
1、 SqlSessionFactoryBuilder
它的作用只是通过配置文件创建SqlSessionFactory,所以只要创建出SqlSessionFactory,它就可以销毁了。所以说,它的生命周期是在方法之内。
2、 SqlSessionFactory
它的作用是创建SqlSession的工厂,工厂一旦创建,除非应用停掉,不要销毁。
所以说它的生命周期是在应用范围内。这里可以通过单例模式来管理它。
在mybatis整合spring之后,最好的处理方式是把SqlSessionFactory交由spring来做单例管理。
3、 SqlSession
SqlSession是一个面向用户(程序员)的接口,它的默认实现是DefaultSqlSession。
Mybatis是通过SqlSession来操作数据库的。SqlSession中不仅包含要处理的SQL信息,还包括一些数据信息,所以说它是线程不安全的,因此它最佳的生命周期范围是在方法体之内。
2,原始dao开发方式
需要向dao实现类中注入SqlSessionFactory,在方法体内通过SqlSessionFactory创建SqlSession
要注意SqlSession和SqlSessionFactory的生命周期。
Dao实现类代码
public class UserDaoImpl implements UserDao {
//注入SqlSessionFactory
private SqlSessionFactory sqlSessionFactory;
//使用构造方法来初始化SqlSessionFactory
public UserDaoImpl(SqlSessionFactory sqlSessionFactory){
this.sqlSessionFactory = sqlSessionFactory;
}
@Override
public User findUserById(int id) {
//通过工厂,在方法内部获取SqlSession,这样就可以避免线程不安全
SqlSession sqlSession = sqlSessionFactory.openSession();
//返回结果集
return sqlSession.selectOne("test.findUserById", id);
}
@Override
public List<User> findUsersByName(String username) {
//通过工厂,在方法内部获取SqlSession,这样就可以避免线程不安全
SqlSession sqlSession = sqlSessionFactory.openSession();
return sqlSession.selectList("test.findUsersByName", username);
}
@Override
public void insertUser(User user) {
//通过工厂,在方法内部获取SqlSession,这样就可以避免线程不安全
SqlSession sqlSession = sqlSessionFactory.openSession();
sqlSession.insert("test.insertUser", user);
}
}
编写测试代码
原始dao开发存在一些问题:
1,存在一定量的模板代码。比如:通过SqlSessionFactory创建SqlSession;调用SqlSession的方法操作数据库;关闭Sqlsession。
2,存在一些硬编码。调用SqlSession的方法操作数据库时,需要指定statement的id,这里存在了硬编码
三,Mapper代理开发方式
Mapper代理的开发方式,程序员只需要编写mapper接口(相当于dao接口)即可。Mybatis会自动的为mapper接口生成动态代理实现类。
不过要实现mapper代理的开发方式,需要遵循一些开发规范。
1,开发规范
1、 mapper接口的全限定名要和mapper映射文件的namespace的值相同。
2、 mapper接口的方法名称要和mapper映射文件中的statement的id相同;
3、 mapper接口的方法参数只能有一个,且类型要和mapper映射文件中statement的parameterType的值保持一致。
4、 mapper接口的方法参数如输入多个时接口中必需加入注解@Param,且mapper映射文件中不需要parameterType
5、 mapper接口的返回值类型要和mapper映射文件中statement的resultType值或resultMap中的type值保持一致;
通过规范式的开发mapper接口,可以解决原始dao开发当中存在的问题:
1、 模板代码已经去掉;
2、 剩下去不掉的操作数据库的代码,其实就是一行代码。这行代码中硬编码的部分,通过第一和第二个规范就可以解决。
即:
1. UserMapper.xml 名必需与接口名一至
2. SqlMapConfig.xml 与接口必需在同包下
3.-namespace命名空间 必需是接口的全限定名
3.statement id 必需与接口中的方法名一致
4.接口中的返回值必需与resultType类型一致
5.方法参数类型必需与parameterType类型一致
6.方法能数只能传一个参数
7.如果需要传递多个,加上注解,在mapper.xml文件中不需要定义parameterType
2,程序编写
1,
步骤中的1、2都在入门程序中进行了编写,此处不需要重新编写。
2,编写mapper映射文件
重新定义mapper映射文件UserMapper.xml(内容同Users.xml,除了namespace的值),放到新创建的目录mapper下
3,加载mapper映射文件
<!-- 加载mapper -->
<mappers>
<mapper resource="mapper/UserMapper.xml"/>
</mappers>
4,编写mapper接口
5,编写测试代码
// 声明全局的SqlSessionFactory
private static SqlSessionFactory sqlSessionFactory ;
@BeforeClass
public static void setUpBeforeClass() throws Exception {
// 1、读取配置文件
String sqlMapConfig = "SqlMapConfig.xml";
InputStream is = Resources.getResourceAsStream(sqlMapConfig);
// 2、根据配置文件创建SqlSessionFactory
sqlSessionFactory= new SqlSessionFactoryBuilder().build(is);
}
@Test
public void testFindUserById() {
// 创建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 通过SqlSession,获取mapper接口的动态代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 调用mapper对象的方法
User user = userMapper.findUserById(1);
System.out.println(user);
// 关闭SqlSession
sqlSession.close();
}
三,Mybatis全局配置文件
SqlMapConfig.xml是mybatis的全局配置文件,它的名称可以是任意命名的。
全部配置内容
SqlMapConfig.xml的配置内容和顺序如下(顺序不能乱):
Properties(属性)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境信息集合)
environment(单个环境信息)
transactionManager(事物)
dataSource(数据源)
mappers(映射器)
Properties
SqlMapConfig.xml使用properties标签后,如下所示:
<!-- 通过properties标签,读取java配置文件的内容 -->
<properties resource="db.properties" />
<!-- 配置mybatis的环境信息 -->
<environments default="development">
<environment id="development">
<!-- 配置JDBC事务控制,由mybatis进行管理 -->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置数据源,采用dbcp连接池 -->
<dataSource type="POOLED">
<property name="driver" value="${db.driver}"/>
<property name="url" value="${db.url}"/>
<property name="username" value="${db.username}"/>
<property name="password" value="${db.password}"/>
</dataSource>
</environment>
</environments>
注:
mybatis将按照下面的顺序加载属性:
使用${},可以引用已经加载的java配置文件中的信息。
Properties标签体内定义的属性首先被读取
Properties引用的属性会被读取,如果发现上面已经有同名的属性了,那后面会覆盖前面的值
parameterType接收的值会最后被读取,如果发现上面已经有同名的属性了,那后面会覆盖前面的值
所以说,mybatis读取属性的顺序由高到低分别是:parameterType接收的属性值、properties引用的属性、properties标签内定义的属性。
Settings
typeAliases
SqlMapConfig.xml配置信息如下:
<!-- 定义别名 -->
<typeAliases>
<!-- 单个定义别名 -->
<typeAlias type="com.mybatis.po.User" alias="user"/>
<!-- 批量定义别名(推荐) -->
<!-- [name]:指定批量定义别名的类包,别名为类名(首字母大小写都可) -->
<package name="com.mybatis.po"/>
</typeAliases>
mappers
使用相对于类路径的资源
如:
<mapper resource="sqlmap/User.xml" />
使用完全限定路径
如:
<mapper url="file:///D:\workspace_spingmvc\mybatis_01\config\sqlmap\User.xml" />
使用mapper接口的全限定名
如:
<mapper class="com.mybatis.mapper.UserMapper"/>
注意:此种方法要求mapper接口和mapper映射文件要名称相同,且放到同一个目录下;
注册指定包下的所有映射文件
如:
<package name="com.mybatis.mapper"/>
四,Mybatis映射文件
1,输入映射
1.1 ParameterType
指定输入参数的java类型,可以使用别名或者类的全限定名。它可以接收简单类型、POJO、HashMap
传递简单类型
参考入门需求:根据用户ID查询用户信息:
传递POJO对象
参考入门需求:添加用户:
传递POJO包装对象
开发中通过pojo传递查询条件 ,查询条件是综合的查询条件,不仅包括用户查询条件还包括其它的查询条件(比如将用户购买商品信息也作为查询条件),这时可以使用包装对象传递输入参数。
例:
1,需求:
综合查询用户信息,需要传入查询条件复杂,比如(用户信息、订单信息、商品信息)。
2,定义包装对象:
一般User.java类要和数据表表字段一致,最好不要在这里面添加其他字段,如果使用mybatis的逆向工程时,会根据表结构,生成po类,如果在po类中扩展字段,此时会被覆盖掉。
所以针对要扩展的po类,我们需要创建一个扩展类,来继承它。
MyBatis的逆向工程指利用MyBatis Generator(MBG),可以快速的根据表生成对应的映射文件,接口,以及bean类。
3,编写Mapper接口
//通过包装类来进行复杂的用户信息综合查询
public List<UserExt> findUserList(UserQueryVO userQueryVO);
4,编写mapper映射文件
<!-- 通过包装类来进行复杂的用户信息综合查询 -->
<select id="findUserList" parameterType="userQueryVO" resultType="userExt">
SELECT * FROM USER WHERE sex=#{userExt.sex} AND username LIKE '%${userExt.username}%'
</select>
注:入参的类型变为UserQueryVO、结果集的类型变为UserExt,#{}里面的参数变为UserQueryVO对象中的userExt属性的sex和username子属性。
5,编写测试代码
传递HashMap
同传递POJO对象一样,map的key相当于pojo的属性
1,映射文件
<!-- 传递hashmap综合查询用户信息 -->
<select id="findUserByHashmap" parameterType="hashmap" resultType="user">
select * from user where id=#{id} and username like '%${username}%'
</select>
2,测试代码
Public void testFindUserByHashmap()throws Exception{
//获取session
SqlSession session = sqlSessionFactory.openSession();
//获限mapper接口实例
UserMapper userMapper = session.getMapper(UserMapper.class);
//构造查询条件Hashmap对象
HashMap<String, Object> map = new HashMap<String, Object>();
map.put("id", 1);
map.put("username", "管理员");
//传递Hashmap对象查询用户列表
List<User>list = userMapper.findUserByHashmap(map);
//关闭session
session.close();
}
异常测试:
传递的map中的key和sql中解析的key不一致。
测试结果没有报错,只是通过key获取值为空。
2,输出映射
2.1 resultType
1, 使用方法
使用resultType进行结果映射时,查询的列名和映射的pojo属性名完全一致,该列才能映射成功。
如果查询的列名和映射的pojo属性名全部不一致,则不会创建pojo对象;
如果查询的列名和映射的pojo属性名有一个一致,就会创建pojo对象。
2,输出简单类型
当输出结果只有一列时,可以使用ResultType指定简单类型作为输出结果类型。
例:
1,需求
查询用户总数
2,Mapper映射文件
<!-- 查询用户信息总数-->
<select id="findUsersCount" parameterType="UserVo"
resultType="int">
SELECT count(1) FROM USER WHERE sex = #{userExt.sex} AND username LIKE '%${userExt.username}%'
</select>
3,Mapper接口
//综合查询用户信息总数。学习:resultType输出简单类型
public int findUsersCount(UserVo vo);
4,测试代码
3,输出POJO单个对象和列表
注意:输出单个pojo对象和pojo列表(盛放pojo对象)时,mapper映射文件中的resultType的类型是一样的,mapper接口的方法返回值不同。
1,Mapper映射文件
Mapper映射文件是同一个
<select id="findUsersByName" parameterType="java.lang.String" resultType="com.mybatis.po.User">
SELECT * FROM USER WHERE username LIKE '%${value}%'
</select>
2,Mapper接口
下面看下mapper接口的不同之处
1、 输出单个pojo对象
//根据用户名称来模糊查询用户信息
public User findUsersByName(String username);
2、 输出pojo列表
//根据用户名称来模糊查询用户信息列表
public List<User> findUsersByName(String username);
总结:同样的mapper映射文件,返回单个对象和对象列表时,mapper接口在生成动态代理的时候,会根据返回值的类型,决定调用selectOne方法还是selectList方法。
2.2 resultMap
resultMap可以进行高级结果映射(一对一、一对多映射,后面课程讲解)。
1,使用方法
如果查询出来的列名和属性名不一致,通过定义一个resultMap将列名和pojo属性名之间作一个映射关系。
定义resultMap
使用resultMap作为statement的输出映射类型。
2, 需求
把下面SQL的输出结果集进行映射
SELECT id id_,username username_,sex sex_ FROM USER WHERE id = 1
3,Mapper映射文件
定义resultMap:
<!-- 定义resultMap -->
<!--
[id]:定义resultMap的唯一标识
[type]:定义该resultMap最终映射的pojo对象
[id标签]:映射结果集的唯一标识列,如果是多个字段联合唯一,则定义多个id标签
[result标签]:映射结果集的普通列
[column]:SQL查询的列名,如果列有别名,则该处填写别名
[property]:pojo对象的属性名
-->
<resultMap type="user" id="userResultMap">
<id column="id_" property="id"/>
<result column="username_" property="username"/>
<result column="sex_" property="sex"/>
</resultMap>
定义statement:
<!-- 根据ID查询用户信息 -->
<select id="findUserByIdResultMap" parameterType="int" resultMap="userResultMap">
SELECT id id_,username username_,sex sex_ FROM USER WHERE id = #{id}
</select>
4,Mapper接口定义
//根据ID查询用户信息
public User findUserByIdResultMap(int id);
定义Statement使用resultMap映射结果集时,Mapper接口定义方法的返回值类型为mapper映射文件中resultMap的type类型。
5, 测试代码
@Test
public void findUserByIdResultMapTest() {
// 创建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 通过SqlSession,获取mapper接口的动态代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 调用mapper对象的方法
User user = userMapper.findUserByIdResultMap(1);
System.out.println(user);
// 关闭SqlSession
sqlSession.close();
}
3,动态SQL
通过Mybatis提供的各种动态标签实现动态拼接sql,使得mapper映射文件在编写SQL时更加灵活,方便。常用动态SQL标签有:if、where、foreach;
3.1 If 和 where
If标签:作为判断入参来使用的,如果符合条件,则把if标签体内的SQL拼接上。
注意:用if进行判断是否为空时,不仅要判断null,也要判断空字符串‘’;
Where标签:会去掉条件中的第一个and符号。
1, 需求
用户信息综合查询列表和用户信息综合查询总数这两个statement的定义使用动态SQL
2,映射文件
<!-- 综合查询用户信息,需要传入查询条件复杂,比如(用户信息、订单信息、商品信息) -->
<select id="findUsersByQueryVO" parameterType="com.mybatis.po.QueryUserVO"
resultType="User">
SELECT * FROM USER
<where>
<if test="userExt != null">
<if test="userExt.sex != null and userExt.sex != ''">
AND sex = #{userExt.sex}
</if>
<if test="userExt.username != null and userExt.username != ''">
AND username LIKE '%${userExt.username}%'
</if>
</if>
</where>
</select>
<!-- 综合查询用户信息总数,需要传入查询条件复杂,比如(用户信息、订单信息、商品信息) -->
<select id="findUsersCount" parameterType="QueryUserVO"
resultType="int">
SELECT count(1) FROM USER
<where>
<if test="userExt != null">
<if test="userExt.sex != null and userExt.sex != ''">
AND sex = #{userExt.sex}
</if>
<if test="userExt.username != null and userExt.username != ''">
AND username LIKE '%${userExt.username}%'
</if>
</if>
</where>
</select>
3, Mapper接口
//通过包装类来进行复杂的用户信息综合查询
public List<UserExt> findUserList(UserQueryVO userQueryVO);
//综合查询用户总数
public int findUsersCount(UserQueryVO userQueryVO);
4,测试代码
不传用户名:
输出的SQL如下(也不包含用户名):
通过测试可以得知,打印出的SQL语句确实会随着条件的满足情况而不一样。
3.1 If Set改造
1,映射文件
<!-- 修改 -->
<update id="updateUserSet" parameterType="user">
update t_user
<set>
<if test="uname!=null">
uname =#{uname},
</if>
<if test="birthday!=null">
birthday =#{birthday},
</if>
<if test="sex!=null and sex!=0">
sex =#{sex},
</if>
<if test="address!=null">
address =#{address},
</if>
</set>
where tid=#{tid}
</update>
2,Mapper接口
//修改
public int updateUserSet(User user);
3,测试代码
@Test
public void updateUserSet() {
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = new User();
user.setSex('1');
user.setUname("小3");
user.setAddress(" 刚修改");
user.setTid(5);
userMapper.updateUserSet(user);
sqlSession.close();
}
3.3 SQL片段
Mybatis提供了SQL片段的功能,可以提高SQL的可重用性。
1, 定义SQL片段
使用sql标签来定义一个SQL片段:
<!-- 定义SQL片段 -->
<!--
[sql标签]:定义一个SQL片段
[id]:SQL片段的唯一标识
建议:
1、SQL片段中的内容最好是以单表来定义
2、如果是查询字段,则不要写上SELECT
3、如果是条件语句,则不要写上WHERE
-->
<sql id="select_user_where">
<if test="userExt != null">
<if test="userExt.sex != null and userExt.sex != ''">
AND sex = #{userExt.sex}
</if>
<if test="userExt.username != null and userExt.username != ''">
AND username LIKE '%${userExt.username}%'
</if>
</if>
</sql>
2,引用SQL片段
使用 来引用SQL片段:
<!-- 根据用户id来查询用户信息(使用SQL片段) -->
<!--
[include标签]:引用已经定义好的SQL片段
[refid]:引用的SQL片段id
-->
<select id="findUserList" parameterType="userQueryVO" resultType="userExt">
SELECT * FROM USER
<where>
<include refid="select_user_where"/>
</where>
</select>
<!-- 综合查询用户信息总数,需要传入查询条件复杂,比如(用户信息、订单信息、商品信息) -->
<select id="findUsersCount" parameterType="QueryUserVO"
resultType="int">
SELECT count(1) FROM USER
<where>
<include refid="select_user_where"/>
</where>
</select>
3.4 Foreach
向sql传递数组或List时,mybatis使用foreach解析数组里的参数并拼接到SQL中。
传递pojo对象中的List集合
1,需求
在用户查询列表和查询总数的statement中增加多个id输入查询。
2,SQL
SELECT * FROM user WHERE id IN (1,10,16)
3,定义pojo中的List属性
4,映射文件
<!-- [foreach标签]:表示一个foreach循环 -->
<!-- [collection]:集合参数的名称,如果是直接传入集合参数,则该处的参数名称只能填写[list]。 -->
<!-- [item]:每次遍历出来的对象 -->
<!-- [open]:开始遍历时拼接的串 -->
<!-- [close]:结束遍历时拼接的串 -->
<!-- [separator]:遍历出的每个对象之间需要拼接的字符 -->
<if test="idList != null and idList.size > 0">
<foreach collection="idList" item="id" open="AND id IN (" close=")" separator=",">
#{id}
</foreach>
</if>
5,Mapper接口
//根据用户ID的集合查询用户列表(学习foreach标签之通过POJO对象传ID集合)
public List<UserExt> findUserList(UserQueryVO vo);
6,测试代码
@Test
public void testFindUserList() {
// 创建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 通过SqlSession,获取mapper接口的动态代理对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// 构造QueryUserVO对象
QueryUserVO vo = new QueryUserVO();
// UserExt ext = new UserExt();
// ext.setUsername("小明");
// ext.setSex("1");
// vo.setUserExt(ext);
// 创建用户ID集合,然后设置到QueryUserVO对象中
List<Integer> idList = new ArrayList<Integer>();
idList.add(1);
idList.add(10);
idList.add(16);
vo.setIdList(idList);
// 调用mapper代理对象的方法
List<UserExt> list = mapper.findUserList(vo);
System.out.println(list);
// 关闭SqlSession
sqlSession.close();
}
四,关联查询映射
1,分析数据模型
1,思路
1、 每张表记录的数据内容
分模块对每张表记录的内容进行熟悉,相当于你学习系统需求(功能)的过程。
2、 每张表重要的字段
主键、外键、非空字段
3、 数据库级别表与表的关系
外键关系
4、 表与表之间的业务关系
在分析表与表之间的业务关系时一定要建立 在某个业务意义基础上去分析。
2,图形分析
3,数据库表之间有外键关系的业务关系
user和orders:
user---->orders:一个用户可以创建多个订单,一对多
orders—>user:一个订单只由一个用户创建,一对一
orders和orderdetail:
orders---->orderdetail:一个订单可以包括 多个订单明细,因为一个订单可以购买多个商品,每个商品的购买信息在orderdetail记录,一对多关系
orderdetail–> orders:一个订单明细只能包括在一个订单中,一对一
orderdetail和itesm:
orderdetail—>itesms:一个订单明细只对应一个商品信息,一对一
items–> orderdetail:一个商品可以包括在多个订单明细 ,一对多
4,数据库表之间没有外键关系的业务关系
Orders和items:
这两张表没有直接的外键关系,通过业务及数据库的间接关系分析出它们是多对多的关系。
Orders–> orderdetail –>items:一个订单可以有多个订单明细,一个订单明细对应一个商品,所以一个订单对应多个商品
Items–>orderdetail–>orders:一个商品可以对应多个订单明细,一个订单明细对应一个订单,所以一个商品对应多个订单
User和items:
这两张表没有直接的外键关系,通过业务及数据库的间接关系分析出它们是多对多的关系。
User–>orders–>orderdetail–>items:一个用户有多个订单,一个订单有多个订单明细、一个订单明细对应一个商品,所以一个用户对应多个商品
Items–>orderdetail–>orders–>user:一个商品对应多个订单明细,一个订单明细对应一个订单,一个订单对应一个用户,所以一个商品对应多个用户
2,一对一查询
1,需求:
查询订单信息,关联查询创建订单的用户信息
2,SQL语句
确定查询的主表:订单表
确定查询的关联表:用户表
SELECT
o.id,
o.order_Number,
o.createtime,
o.describer,
o.user_id
userId,
u.uname,
u.address
FROM orders o,t_user u
WHERE o.user_id=u.tid;
3,resultType
复杂查询时,单表对应的po类已不能满足输出结果集的映射。
所以要根据需求建立一个扩展类来作为resultType的类型。
1,创建po类
//通过此类映射订单和用户查询的结果,让此类继承包括 字段较多的pojo类
public class OrdersExt extends Orders{
//添加用户属性
/*USER.username, USER.address */
private String username;
private String address;
}
2,编写mapper接口
创建OrdersMapper接口类,在类中添加以下内容:
// 进行订单信息查询,包括用户的名称和地址信息
public List<OrdersExt> findOrdersUser();
3,编写映射文件
<mapper namespace="com.mybatis.mapper.OrdersMapper">
<!-- 定义查询订单表列名的SQL片段 -->
<sql id="select_orders">
Orders.id,
Orders.user_id,
orders.number,
orders.createtime,
orders.note
</sql>
<!-- 定义查询用户表列名的SQL片段 -->
<sql id="select_user">
user.username,
user.address
</sql>
<!-- 进行订单信息查询,包括用户的名称和地址信息 -->
<select id="findOrdersUser" resultType="OrdersExt">
Select
<include refid="select_orders" />
<include refid="select_user"></include>
from orders,user
where orders.user_id = user.id
</select>
</mapper>
4,加载映射文件
<!-- 批量加载mapper文件,需要mapper接口文件和mapper映射文件名称相同且在同一个包下 -->
<package name="com.mybatis.mapper"/>
5, 编写测试代码
@Test
public void testFindOrdersUser() {
// 创建sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 通过SqlSession构造usermapper的代理对象
OrdersMapper orders qlSession.getMapper(OrdersMapper.class);
// 调用usermapper的方法
// 释放SqlSession
sqlSession.close();
}
4,resultMap
1,修改po类
在Orders类中,添加User对象
public class Orders {
private Integer id;
private Integer userId;
private String number;
private Date createtime;
private String note;
//用户信息
private User user;
2,编写mapper接口
// 进行订单信息查询,包括用户的名称和地址信息(resultMap)
public List<OrdersExt> findOrdersUserRstMap();
3,编写映射文件
<!-- 进行订单信息查询,包括用户的名称和地址信息 (ResultMap) -->
<select id="findOrdersUserRstMap" resultMap="OrdersUserRstMap">
Select
<include refid="select_orders" />
,
<include refid="select_user"></include>
from orders,user
where orders.user_id = user.id
</select>
<!-- 定义orderUserResultMap -->
<resultMap type=" com.mybatis.po.Orders" id="OrdersUserRstMap">
<id column="id" property="id" />
<result column="user_id" property="userId" />
<result column="number" property="number" />
<result column="createtime" property="createtime" />
<result column="note" property="note" />
<!-- 映射一对一关联关系的用户对象-->
<!--
property:指定关联对象要映射到Orders的哪个属性上
javaType:指定关联对象所要映射的java类型
-->
<!-- id标签:指定关联对象结果集的唯一标识,很重要,不写不会报错,但是会影响性能 -->
<association property="user" javaType="com.mybatis.po.User">
<id column="user_id" property="id" />
<result column="username" property="username" />
<result column="address" property="address" />
</association>
</resultMap>
4,编写测试代码
@Test
public void testFindOrdersUserRstMap() {
// 创建sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 通过SqlSession构造usermapper的代理对象
OrdersMapper ordersMapper = sqlSession.getMapper(OrdersMapper.class);
// 调用usermapper的方法
List<Orders> list = ordersMapper.findOrdersUserRstMap();
//此处我们采用debug模式来跟踪代码,然后验证结果集是否正确
System.out.println(list);
// 释放SqlSession
sqlSession.close();
}
5,一对一小结
实现一对一查询:
resultType:使用resultType实现较为简单,如果pojo中没有包括查询出来的列名,需要增加列名对应的属性,即可完成映射。
如果没有查询结果的特殊要求建议使用resultType。
resultMap:需要单独定义resultMap,实现有点麻烦,如果对查询结果有特殊的要求,使用resultMap可以完成将关联查询映射pojo的对象属性中。
resultMap可以实现延迟加载,resultType无法实现延迟加载。
3,一对多查询
一对多查询和一对一查询的配置基本类似。只是如果使用resultMap的话,映射一对多关联关系要使用collection标签。