一、MyBatis介绍
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
特性:
1.sql集中管理
之前都是sql语句和java代码耦合在一起,Mybatis解耦 sql语句和java代码,将sql语句分离出来单独管理。
sql语句集中管理的地方称为映射文件,一般将不同功能的sql语句放到不同的映射文件中。
2.动态sql
根据判断条件的不同,执行的不同的sql语句。
3.高级映射
输入映射 和 输出映射。
输入映射:
输出映射:
二、入门案例1
映射文件中sql语句唯一性保证:通过映射文件id加上sql语句id保证,其中不同映射文件的sql语句id可以相同。
2.1导包
mybatis
mysql-connector-java
junit
<dependencies>
<!--使用mybatis,如果使用 Maven 来构建项目,则需将下面的依赖代码置于 pom.xml 文件中-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
</dependencies>
2.2mybatis配置文件
mybatis.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="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/j23_db?useUnicode=true&characterEncoding=utf-8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<!--加载的classpath下的映射文件-->
<mapper resource="mapper/UserMapper.xml"/>
</mappers>
</configuration>
2.3映射文件
UserMapper.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映射文件的唯一id-->
<mapper namespace="userNamespace">
<select id="selectUsernameById" resultType="string">
select username from j23_user_t where id = #{id}
</select>
</mapper>
2.4通过映射文件的namespace和sql语句id执行sql语句
SqlSessionFactoryBuilder (加载配置文件)→ sqlSessionFactory → sqlSession
package com.cskaoyan;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
public class MyTest {
@Test
public void mytest() throws IOException {
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//加载classpath下的mybatis的配置文件
InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");
SqlSessionFactory factory = builder.build(inputStream);
SqlSession sqlSession = factory.openSession();
//根据id查询username → select username from j23_user_t where id = ?
//如果查询结果记录数只有一条,使用selectOne
//selectOne的两个参数:参数1 String 命名空间.sql标签id ,参数2 Object 为sql语句提供参数
String username = sqlSession.selectOne("userNamespace.selectUsernameById", 1);
System.out.println("username = " + username);
}
}
三、sqlSession的方法
3.1查询 select
3.1.1javabean
根据id查询user:
创建User对象并且在数据库中创建相对应的表。
@Data
public class User {
Integer id;
String username;
String password;
Integer age;
String gender;
}
UserMapper.xml配置sql语句id
<!--查询单条user这个javabean-->
<!--resultType是查询必须的 → 不能省略-->
<!--resultType javabean类型 → javabean的全类名-->
<select id="selectUserById" resultType="com.xxx.bean.User">
select id,username,password,age,gender from j23_user_t where id = #{id}
</select>
测试类
@Test
public void mytest2() throws IOException {
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis.xml");
SqlSessionFactory sqlSessionFactory = builder.build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
//返回值类型和resultType对应
User user = sqlSession.selectOne("userNamespace.selectUserById", 1);
System.out.println("user = " + user);
}
补充
通过单元测试的注解简化代码,可以将之前两个单元测试方法中的重复代码整合到一个方法中。
首先,了解junit的几个注解。
import org.junit.*;
public class JunitTest {
@Before
public void initEveryTest(){
System.out.println("hello world");
}
@After
public void afterEveryTest(){
System.out.println("close");
}
@BeforeClass
public static void initFirst(){
System.out.println("最初的xxx");
}
@AfterClass
public static void last(){
System.out.println("最终的xxx");
}
@Test
public void mytest1(){
//System.out.println("hello world");
System.out.println("mytest1");
}
@Test
public void mytest2(){
//System.out.println("hello world");
System.out.println("mytest2");
}
@Test
public void mytest3(){
//System.out.println("hello world");
System.out.println("mytest3");
}
}
从测试结果可以看出来@Before、@After注解会在每个单元测试方法执行之前和之后都执行一次,@BeforeClass、@AfterClass只会在所有单元测试方法执行之前和执行之后只执行一次。
利用这几个注解的特性,我们可以将这段重复的代码放到@Before注解的方法中。
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis.xml");
SqlSessionFactory sqlSessionFactory = builder.build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
由于sqlSession不是线程安全的,所以每次都要获得一个sqlSession,所以写到@Before注解的方法中。
而sqlSessionFactory是线程安全的,可以只获取一次,所以写到@BeforeClass注解的方法中。
public class MyModifiedTest {
//提取出来,作为全局变量使用
SqlSession sqlSession;
static SqlSessionFactory factory;
@BeforeClass
public static void initFactory() throws IOException {
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//加载classpath下的mybatis的配置文件
InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");
factory = builder.build(inputStream);
}
@Before
public void initSqlSession(){
sqlSession = factory.openSession();
}
@Test
public void mytest(){
User user = sqlSession.selectOne("userNamespace.selectUserById", 1);
System.out.println("user = " + user);
}
}
3.1.2 list
<!--查询list-->
<!--如果是查询多条数据,resultType 依然写的是单条记录对应的类型-->
<select id="selectUsers" resultType="com.xxx.bean.User">
select id,username,password,age,gender from j23_user_t
</select>
@Test
public void mytest3(){
List<User> objects = sqlSession.selectList("userNamespace.selectUsers");
for (User object : objects) {
System.out.println(object);
}
}
3.2新增 insert
增删改最后需要提交,所以在测试类中添加一个方法进行提交。
@After
public void commit(){
if (sqlSession != null){
sqlSession.commit();
sqlSession.close();
}
}
<!--新增insert-->
<!--传入的第二个参数object为user 这样的一个javabean对象 → #{} → javabean的成员变量名(get方法)-->
<insert id="insertUser" >
insert into j23_user_t (username,password,age,gender)
values
(#{username},#{password},#{age},#{gender})
</insert>
@Test
public void mytest4(){
User user = new User();
user.setUsername("xiaoliang");
user.setPassword("123456");
user.setAge(15);
user.setGender("util man");
int insert = sqlSession.insert("userNamespace.insertUser", user);
System.out.println("insert = " + insert );
//if (sqlSession != null){
// sqlSession.commit();
// sqlSession.close();
//}
}
3.3修改 update
<!--修改update-->
<update id="updateUser">
update j23_user_t set gender = #{gender} where id = #{id}
</update>
@Test
public void mytest5(){
//根据id更新 gender
User user = new User();
user.setId(5);
user.setGender("util person");
int update = sqlSession.update("userNamespace.updateUser", user);
//if (sqlSession != null){
// sqlSession.commit();
// sqlSession.close();
//}
}
3.4删除 delete
<!--删除delete-->
<delete id="deleteUser">
delete from j23_user_t where id = #{id}
</delete>
@Test
public void mytest6(){
int delete = sqlSession.delete("userNamespace.deleteUser", 5);
}
3.5 小结
1、sqlSession执行不同的方法做增删改查 → 两个参数
→ String :命名空间+id → 指向唯一的sql语句
→ object:为sql语句提供参数 → #{}
基本类型、包装类、字符串:#{}里是任意写
javabean:#{}写的是javabean的成员变量名
2、不同的操作对应的标签不同insert、delete、update、select
3、select标签 → 一定要写resultType属性 不能省略 → resultType里写的是单条记录的类型
4、增删改返回值均为int → 影响数据库的记录数 → 不能够写resultType
四、配置项
4.1properties
4.1.1单项配置
4.1.2引入文件
4.2settings
4.2.1缓存cache
mybatis缓存有两级。
4.2.1.1 一级缓存
默认开启一级缓存,SQLSession级别。
怎样验证一级缓存:使用同一查询,第二次不会进行预编译(Preparing)。
一级缓存在执行sqlSession的commit时失效。
4.2.1.2 二级缓存
默认开启,但是我们不能直接使用。需要我们做一下操作:
1、settings里的cacheEnabled为true(默认的)
2、查询结果要序列化
3、映射文件中要开启缓存
测试二级缓存:
第一次要进行预编译,sqlSession提交或者关闭后,查询结果被放到二级缓存中,第二次调用查询方法,命中二级缓存,缓存命中率为1/(1+1)=0.5;第三次调用查询方法,命中二级缓存,缓存命中率(1+1)/(1+1+1)=0.6666……;第四次调用查询方法,命中二级缓存,缓存命中率(1+1+1)/(1+1++1)=0.75;第五次调用查询方法,命中二级缓存,缓存命中率(1+1+1+1)/(1+1++1+1)=0.8.
二级缓存是mapper级别的缓存,多个SqlSession可以去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。
二级缓存与一级缓存区别,二级缓存的范围更大,多个sqlSession可以共享一个UserMapper的二级缓存区域。
UserMapper有一个二级缓存区域(按namespace分),其它mapper也有自己的二级缓存区域(按namespace分)。每一个namespace的mapper都有一个二缓存区域,两个mapper的namespace如果相同,这两个mapper执行sql查询到数据将存在相同的二级缓存区域中。
4.2.2懒加载
4.2.2.1没有配置的情况下懒加载是否开启
4.2.2.2开启懒加载后默认为懒加载
注意测试时,不要输出user或者使用debug模式,因为会调用到toString方法,toString方法中会调用getUserDetail方法,就会对查询UserDetail语句进行预编译,此时不是没有使用懒加载,而是已经调用到相应的方法,自然会执行查询UserDetail语句的预编译。
4.2.2.3想要立即加载
4.3typeAliases
type → 别名
resultType javabean的全类名 → 别名
4.3.1对单条配置别名
4.3.2批量配置别名
4.3.3mybatis提供的别名
关注基本类型、包装类、字符串
4.4typehandlers
输入输出映射过程中参数类型不匹配,使用typehandler类型转换。
举个例子:
User的Javabean:
class User{
Integer id;
UserDetail userDetail; //UserDetail
Integer[] orderIds;
}
User在数据库中的数据表user_t :
id,user_detail(varchar → json) ,order_ids(varchar → json)
tip:需要经常修改查询更新的,要保存成表的形式,而不是像上面的user_detail保存成字符串的格式。
如果直接执行查询语句select id,user_detail as userDetail,order_ids as orderIds from user_t进行封装,数据是封装不进去的。这个时候就可以用到typehandler在中间进行一个数据格式的转换,成功将数据封装到javabean对象中。
typehandler
分析user_detail和userDetail
jdbcType.varchar ↔ javaType.UserDetail
输出映射:jdbcType → javaType(比如查询结果的封装)
输入映射:javaType → jdbcType (比如更新数据)
4.4.1mybatis配置文件中配置typehandler
<typeHandlers>
<!--可以写成单个handler的形式:handler要写typehandler的全类名-->
<!--<typeHandler handler="com.qyt.typehandler.UserDetailTypehandler"/>-->
<!--也可以写成package包的形式-->
<package name="com.qyt.typehandler"/>
</typeHandlers>
4.4.2定义typehandler
4.4.3输入映射的方法
4.4.4输出映射的方法
@MappedTypes(UserDetail.class) //javabean中成员变量类型
@MappedJdbcTypes(JdbcType.VARCHAR) //表中某列的类型
//BaseTypehandler中的泛型写MappedTypes里的类型 → javabean中的成员变量的类型
public class UserDetailTypehandler extends BaseTypeHandler<UserDetail> {
ObjectMapper objectMapper = new ObjectMapper();
//输入映射的过程 → 为预编译的sql语句提供参数
//
@SneakyThrows
@Override
public void setNonNullParameter(PreparedStatement preparedStatement, int index, UserDetail userDetail, JdbcType jdbcType) throws SQLException {
String value = objectMapper.writeValueAsString(userDetail);
preparedStatement.setString(index,value);
}
//get方法 → 结果集的封装
@SneakyThrows
@Override
public UserDetail getNullableResult(ResultSet resultSet, String columnLabel) throws SQLException {
System.out.println("string = " + columnLabel);
String result = resultSet.getString(columnLabel);
return parseResult(result);
}
@SneakyThrows
@Override
public UserDetail getNullableResult(ResultSet resultSet, int index) throws SQLException {
String result = resultSet.getString(index);
return parseResult(result);
}
@SneakyThrows
@Override
public UserDetail getNullableResult(CallableStatement callableStatement, int index) throws SQLException {
String result = callableStatement.getString(index);
return parseResult(result);
}
private UserDetail parseResult(String result) throws JsonProcessingException {
return objectMapper.readValue(result,UserDetail.class);
}
}
4.4.5 测试
查询数据:
public interface UserMapper {
List<User> selectUserById(@Param("id") Integer id);
}
@Test
public void mytest1(){
List<User> users = userMapper.selectUserById(1);
System.out.println(users);
}
<mapper namespace="com.qyt.mapper.UserMapper">
<select id="selectUserById" resultType="com.qyt.bean.User">
select id,username,password,age,gender,update_date as updateDate,user_detail as userDetail
from j23_user_t where id = #{id}
</select>
</mapper>
更新数据:
public interface UserMapper {
int updateUserDetailById(@Param("id") Integer id, @Param("userDetail") UserDetail userDetail);
}
@Test
public void mytest2(){
UserDetail userDetail = new UserDetail();
userDetail.setEmail("1156986445@qyt.com");
userDetail.setPhone("1594485326");
int i = userMapper.updateUserDetailById(2, userDetail);
System.out.println(i);
}
<update id="updateUserDetailById">
update j23_user_t set user_detail = #{userDetail} where id = #{id}
</update>
数据类型不匹配经过typehandler进行类型转换后,可以进行封装。
4.5mappers
4.5.1单个文件
相同目录结构
mybatis.xml
<mappers>
<!--配置的是接口
→ 接口对应映射文件 → 接口的全类名:namespace
→ 接口中的方法对应 sql语句的id
→ 通过调用接口中的方法 → 唯一与之对应的sql语句
对应关系:
1、接口和映射文件要在同一级目录,并且同名
2、命名空间 为接口的全类名
3、接口中的方法名 对应映射文件中的sql语句标签的id
注意事项:同一个映射文件里 标签id不能重复
-->
<mapper class="com.cskaoyan.mapper.UserMapper"/>
</mappers>
UserMapper.xml
<!--namespace与接口UserMapper的全类名相对应-->
<mapper namespace="com.cskaoyan.mapper.UserMapper">
<!--select的id与接口的方法名相对应-->
<select id="selectUsernameById" resultType="string">
select username from j23_user_t where id = #{id}
</select>
</mapper>
UserMapper.java
public interface UserMapper {
String selectUsernameById(Integer id);
//接口中不允许重载
//String selectUsernameById(String username,String password);
}
4.5.2多个文件
mybatis.xml
<mappers>
<!--<mapper class="com.cskaoyan.mapper.UserMapper"/>-->
<package name="com.cskaoyan.mapper"/>
</mappers>
4.5.3如何使用接口及其方法(阶段性成果)
@Test
public void mytest1(){
//mybatis为UserMapper接口提供了一个代理对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
String username = mapper.selectUsernameById(2);namespace id 参数 结果
System.out.println("username = " + username);
}
五、log4j 日志框架
记录,mybatis支持 → 日志输出
5.1引入依赖
5.2引入配置文件
log4j.properties
#Appender
#org.apache.log4j.ConsoleAppender(控制台)
#org.apache.log4j.FileAppender(文件)
#org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件)
#org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件)
#org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的地方)
### direct log messages to stdout ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
### direct messages to file hibernate.log ###
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=d://21mybatis.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
#org.apache.log4j.HTMLLayout(以HTML表格形式布局)
#org.apache.log4j.PatternLayout(可以灵活地指定布局模式)
#org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串)
#org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等信息)
log4j.appender.file2 = org.apache.log4j.FileAppender
log4j.appender.file2.File=d://file2.log
log4j.appender.file2.layout=org.apache.log4j.SimpleLayout
#log4j.appender.file2.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
### DEBUG、INFO、WARN、ERROR和FATAL
#日志级别+输出appender
log4j.rootLogger=debug ,file,stdout,file2
5.2.1配置文件
5.2.1.1appender
#配置文件 是 key=value的格式
#Appender #以什么形式进行输出
5.2.1.2layout
输出内容的格式
log4j配置文件中的一条语句:
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
- %d 表示时间,{ABSOLUTE} 是时间格式
- %p 日志级别,比如DEDUG
%5p 表示日志级别占用5个字符 - %c 表示类别信息
{数字} 类别信息输出多少级 → 从后往前 - %L 行号:代码中的第多少行输出的
- %m 表示message消息
- %n 表示换行
日志级别:
debug、info、warn、error、fatal
级别越高,内容越少 → 字数越少,事情越大
级别越低,内容越多 → 信息量越大
低级别会输出当前级别以及更高级别的内容
自行输出日志:
import org.apache.log4j.Logger;
import org.junit.Test;
public class LogTest {
Logger logger = Logger.getLogger(LogTest.class);
@Test
public void mytest(){
logger.debug("debug");
logger.info("info");
logger.warn("warn");
logger.error("error");
logger.fatal("fatal");
}
}
六、高级映射
6.1输入映射
为sql语句提供参数 → #{}里面填什么
接口中的方法传入什么样的参数相对应的mapper配置文件中的#{}里面写什么。
6.1.1单个参数
6.1.1.1基本类型、包装类、字符串
#{}里任意写
6.1.1.2javabean或map
#{}里写javabean的成员变量名或map的key
→ 实际上使用的javabean的get方法,或map的get
6.1.2多个参数
#{}中写什么
param1,param2…paramn
arg0,arg1…arg(n-1)
UserMapper接口中的方法
//多个参数
// param1 param2 param3
// arg0 arg1 arg2
int selectIdByUser(String username, String password,Integer age);
// username 通过字符串传入
// password 通过map传入
// age 通过user传入
int selectIdByUserMulti(String username,Map map,User user);
UserMapper.xml
<!--多个参数-->
<!--<select id="selectIdByUser" resultType="int">
select id from j23_user_t where username = #{param1} and password = #{param2} and age = #{param3}
</select>-->
<select id="selectIdByUser" resultType="int">
select id from j23_user_t where username = #{arg0} and password = #{arg1} and age = #{arg2}
</select>
<select id="selectIdByUserMulti" resultType="int">
select id from j23_user_t where username = #{param1} and password = #{param2.password} and age = #{param3.age}
</select>
测试方法
/**
* 多个参数
*/
@Test
public void mytest3(){
int id = mapper.selectIdByUser("laotan", "456", 22);
System.out.println(id);
String username = "laotan";
Map map = new HashMap();
map.put("password", "456");
User user = new User();
user.setAge(22);
int id2 = mapper.selectIdByUserMulti(username, map, user);
System.out.println(id2);
}
日志控制台输出
14:56:51,411 DEBUG com.cskaoyan.mapper.UserMapper.selectIdByUser:137 - ==> Preparing: select id from j23_user_t where username = ? and password = ? and age = ?
14:56:51,480 DEBUG com.cskaoyan.mapper.UserMapper.selectIdByUser:137 - ==> Parameters: laotan(String), 456(String), 22(Integer)
14:56:51,533 DEBUG com.cskaoyan.mapper.UserMapper.selectIdByUser:137 - <== Total: 1
2
14:56:51,535 DEBUG com.cskaoyan.mapper.UserMapper.selectIdByUserMulti:137 - ==> Preparing: select id from j23_user_t where username = ? and password = ? and age = ?
14:56:51,536 DEBUG com.cskaoyan.mapper.UserMapper.selectIdByUserMulti:137 - ==> Parameters: laotan(String), 456(String), 22(Integer)
14:56:51,538 DEBUG com.cskaoyan.mapper.UserMapper.selectIdByUserMulti:137 - <== Total: 1
6.1.3使用注解@Param
接口中方法的形参上 → 写了什么,就用什么
@Param注解的value属性中写了什么值 → #{}中就用什么值
以多个参数为例:
UserAnnotationMapper接口方法
//多个参数
int selectIdByUser(@Param("username") String username, @Param("password") String password, @Param("age") Integer age);
// username 通过字符串传入
// password 通过map传入
// age 通过user传入
int selectIdByUserMulti(@Param("username") String username, @Param("map") Map map, @Param("user") User user);
UserAnnotationMapper.xml
<!--多个参数-->
<select id="selectIdByUser" resultType="int">
select id from j23_user_t where username = #{username} and password = #{password} and age = #{age}
</select>
<select id="selectIdByUserMulti" resultType="int">
select id from j23_user_t where username = #{username} and password = #{map.password} and age = #{user.age}
</select>
测试方法
@Test
public void mytest3(){
int id = mapper.selectIdByUser("laotan", "456", 22);
System.out.println(id);
String username = "laotan";
Map map = new HashMap();
map.put("password", "456");
User user = new User();
user.setAge(22);
int id2 = mapper.selectIdByUserMulti(username, map, user);
System.out.println(id2);
}
日志控制台输出
15:04:08,964 DEBUG com.cskaoyan.mapper.UserAnnotationMapper.selectIdByUser:137 - ==> Preparing: select id from j23_user_t where username = ? and password = ? and age = ?
15:04:09,028 DEBUG com.cskaoyan.mapper.UserAnnotationMapper.selectIdByUser:137 - ==> Parameters: laotan(String), 456(String), 22(Integer)
15:04:09,053 DEBUG com.cskaoyan.mapper.UserAnnotationMapper.selectIdByUser:137 - <== Total: 1
2
15:04:09,055 DEBUG com.cskaoyan.mapper.UserAnnotationMapper.selectIdByUserMulti:137 - ==> Preparing: select id from j23_user_t where username = ? and password = ? and age = ?
15:04:09,056 DEBUG com.cskaoyan.mapper.UserAnnotationMapper.selectIdByUserMulti:137 - ==> Parameters: laotan(String), 456(String), 22(Integer)
15:04:09,060 DEBUG com.cskaoyan.mapper.UserAnnotationMapper.selectIdByUserMulti:137 - <== Total: 1
tip:如果遇到报错中有syntax,说明sql语句有问题,或者格式有问题。
6.2输出映射
sql语句查询结果集的封装 → mapper接口中的方法的返回值来定义结果集。
6.2.1基本类型、包装类、字符串
select和from之间,通常只写一个列或其别名。
单条记录:
根据id查询某个用户名:
接口UserMapper
//通常是查询满足某个条件的 某一列的值或记录数
String selectUsernameById(@Param("id") Integer id);
UserMapper.xml配置文件
<select id="selectUsernameById" resultType="java.lang.String">
select username from j23_user_t where id = #{id}
</select>
测试方法
@Test
public void mytest1(){
String username = mapper.selectUsernameById(2);
logger.info("username = " + username);
}
多条记录:
查询全部用户名:
接口UserMapper
//返回值封装为数组或list之间可以互相转换 → 映射文件里的写法是相同的,返回值写数组或list都行
String[] selectUsernameArray();
//List<String> selectUsernameArray();
List<String> selectUsernameList();
UserMapper.xml配置文件
<select id="selectUsernameArray" resultType="string">
select username from j23_user_t
</select>
<select id="selectUsernameList" resultType="java.lang.String">
select username from j23_user_t
</select>
测试方法
@Test
public void mytest2(){
String[] users1 = mapper.selectUsernameArray();
logger.info(Arrays.asList(users1));
List<String> users2 = mapper.selectUsernameList();
logger.info(users2);
}
单条和多条比较:
接口中的方法的返回值是不同的。
执行的sql语句查询结果允许的记录数不同。
6.2.2javabean
封装数据需满足条件:查询结果的列名和javabean的成员变量名一致。
当javabean成员变量名和数据库中的表的列名不一致时,可以通过as起别名的形式修改查询结果的列名。
表的列名不一定是查询结果的列名,输出映射看的是查询结果的列名。
查询结果的列名和javabean的成员变量名对应 → set方法。
单条记录:
接口UserMapper
//javabean
User selectUserById(@Param("id") Integer id);
UserMapper.xml配置文件
<!--javabean-->
<select id="selectUserById" resultType="com.cskaoyan.bean.User">
select id,username,password,age,gender,update_date as updateDate from j23_user_t where id = #{id}
</select>
测试方法
@Test
public void mytest3(){
User user = mapper.selectUserById(2);
logger.info("user = " + user);
}
多条记录:
接口UserMapper
User[] selectUserArray();
List<User> selectUserList();
UserMapper.xml配置文件
<!--user数组-->
<select id="selectUserArray" resultType="com.cskaoyan.bean.User">
select id,username,password,age,gender,update_date as updateDate from j23_user_t
</select>
<!--userList-->
<select id="selectUserList" resultType="com.cskaoyan.bean.User">
select id,username,password,age,gender,update_date as updateDate from j23_user_t
</select>
测试方法
@Test
public void mytest4(){
User[] users1 = mapper.selectUserArray();
for (User user : users1) {
logger.info("users1:" + user);
}
List<User> users2 = mapper.selectUserList();
for (User user : users2) {
logger.info("users2:" + user);
}
}
单条和多条比较:
接口中的方法的返回值是不同的。
执行的sql语句查询结果允许的记录数不同。
6.2.3resultMap
resultType:查询结果的列名和javabean成员变量名 一致
resultMap:查询结果的列名和javabean成员变量名 对应
之前写的resultType的地方可以替换为resultMap,查询一定要写resultType或resultMap。
column:查询结果的列名
property:javabean的成员变量名(set)
接口UserMapper
//使用resultMap column ↔ property
User selectUserByIdMap(@Param("id") Integer id);
User[] selectUserArrayMap();
List<User> selectUserListMap();
UserMapper.xml配置文件
<!--resultMap-->
<!--resultMap标签的type属性写的就是原先resultType里写的内容 → javabean的全类名、typeAlias-->
<resultMap id="userMap" type="com.cskaoyan.bean.User">
<!--column始终是查询结果的列名-->
<!--property始终是父标签类型javabean的成员变量名-->
<result column="id" property="id"/>
<result column="username" property="username"/>
<result column="password" property="password"/>
<result column="age" property="age"/>
<result column="gender" property="gender"/>
<result column="update_date" property="updateDate"/>
</resultMap>
<!--javabean-->
<select id="selectUserByIdMap" resultMap="userMap">
select id,username,password,age,gender,update_date from j23_user_t where id = #{id}
</select>
<!--user数组-->
<select id="selectUserArrayMap" resultMap="userMap">
select id,username,password,age,gender,update_date from j23_user_t
</select>
<!--userList-->
<select id="selectUserListMap" resultMap="userMap">
select id,username,password,age,gender,update_date from j23_user_t
</select>
测试方法
@Test
public void mytest5(){
User[] users1 = mapper.selectUserArrayMap();
for (User user : users1) {
logger.info("users1:" + user);
}
List<User> users2 = mapper.selectUserListMap();
for (User user : users2) {
logger.info("users2:" + user);
}
}
七、动态sql
通过在映射文件中写标签实现动态sql。
7.1 where标签
将where语句后的内容放入到where标签中
接口UserMapper
User selectUserById(@Param("id") Integer id);
UserMapper.xml
<select id="selectUserById" resultMap="userMap">
select id,username,password,age,gender,update_date from j23_user_t
<where>
id = #{id}
</where>
</select>
测试方法
@Test
public void mytest1(){
User user = mapper.selectUserById(2);
logger.info("user = " + user);
}
7.2 if标签
接口UserMapper
List<User> selectUserByUsernameAndPassword(@Param("username") String username,
@Param("password") String password);
UserMapper.xml
test属性中写if的判断条件,决定是否拼接if标签中的sql语句,test属性中可以使用的值是输入映射的值,即@Param中的value属性值。
<!--利用 if标签来做 条件查询-->
<!--if标签中的test属性写的是条件 → 输入映射可以写的值 → @Param注解的value属性值-->
<select id="selectUserByUsernameAndPassword" resultMap="userMap">
select id,username,password,age,gender,update_date from j23_user_t
<where>
<if test="username != null">
username = #{username}
</if>
<if test="password != null">
and password = #{password}
</if>
</where>
</select>
测试方法
where标签自动将多余的连接词去除,而且如果where标签里没有内容,则不会拼接where字段。
@Test
public void mytest2(){
//username和password都不为null
//Preparing → select id,username,password,age,gender,update_date from j23_user_t where username = ? and password = ?
List<User> users1 = mapper.selectUserByUsernameAndPassword("laotan", "456");
//username不为null,password为null
//Preparing → select id,username,password,age,gender,update_date from j23_user_t where username = ?
List<User> users2 = mapper.selectUserByUsernameAndPassword("laotan", null);
//username为null,password不为null
//未使用where标签前,预编译的sql语句出错,多了一个and
//Preparing → select id,username,password,age,gender,update_date from j23_user_t where and password = ?
//使用where标签后 → select id,username,password,age,gender,update_date from j23_user_t WHERE password = ?
List<User> users3 = mapper.selectUserByUsernameAndPassword(null, "456");
//username和password都为null
//未使用where标签前,预编译的sql语句出错,多了一个where
//Preparing → select id,username,password,age,gender,update_date from j23_user_t where
//使用where标签后 → select id,username,password,age,gender,update_date from j23_user_t
List<User> users4 = mapper.selectUserByUsernameAndPassword(null, null);
}
7.2.1 test中的语法
变量:输入映射可以使用的值 → @Param注解的value属性值
常量:
- 数值:直接写 1 2
- 字符串:单引号 ‘qyt’
- 布尔类型:test = “isMarried”,isMarried值是true或者false
符号:
- 等于: ==
- 不等于: !=
- 大于: gt
- 小于: lt
- 大于等于:大于或等于 ,比如 age gt 20 or age == 20
- 小于等于:小于或等于 ,比如 age lt 20 or age == 20
连接词:多个条件之间的关系
- and 且
- or 或
null值: null
字符串长度:‘字符串’.length
案例:如果年龄≥ 20,查询条件 ≥该年龄
如果年龄< 20,查询条件<该年龄
接口UserMapper
//根据年龄做查询
//如果年龄≥ 20 查询条件 ≥该年龄
//如果年龄< 20 查询条件 <该年龄
List<User> selectUserByAge(@Param("age") Integer age);
UserMapper.xml
<select id="selectUserByAge" resultMap="userMap">
select id,username,password,age,gender,update_date from j23_user_t
<where>
<!--if标签test属性中中的 > , < 的用法-->
<if test="age gt 20 or age == 20">
age >= #{age} <!--xml文件中的 > , < 的用法-->
</if>
<if test="age lt 20">
age < #{age}
</if>
</where>
</select>
测试方法
@Test
public void mytest3(){
List<User> users1 = mapper.selectUserByAge(22);
List<User> users2 = mapper.selectUserByAge(18);
List<User> users3 = mapper.selectUserByAge(23);
}
7.3 choose-when-otherwise组合标签
if标签没有else对应,但有choose-when-otherwise组合标签,相当于if-else。
关于使用,when和otherwise作为choose子标签,when标签中仍然可以使用test属性代表if,otherwise代表的就是else,不写test属性。
接口UserMapper
//使用choose-when-otherwise来实现通过年龄查询用户信息
List<User> selectUserByAgeChooseWhen(@Param("age") Integer age);
UserMapper.xml
<select id="selectUserByAgeChooseWhen" resultMap="userMap">
select id,username,password,age,gender,update_date from j23_user_t
<where>
<choose>
<when test="age gt 20 or age == 20">
age >= #{age}
</when>
<otherwise> <!--注意otherwise标签里面就不要写test属性了,会报错-->
age < #{age}
</otherwise>
</choose>
</where>
</select>
测试方法
@Test
public void mytest4(){
List<User> users1 = mapper.selectUserByAgeChooseWhen(22);
List<User> users2 = mapper.selectUserByAgeChooseWhen(18);
List<User> users3 = mapper.selectUserByAgeChooseWhen(23);
}
7.4 trim标签
属性:
prefixOverrides:如果最前面出现了什么内容,去除掉
suffixOverrides:如果最后面出现了什么内容,去除掉
prefix:最前面拼接一个内容
suffix:最后面拼接一个内容
接口UserMapper
//根据id选择性更新,传入的user对象,哪个成员变量值不为null,就更新该值
int updateSelectUser(@Param("user") User user);
UserMapper.xml
<!--使用trim标签去掉多余的逗号-->
<update id="updateSelectUser">
update j23_user_t
<!--prefix在前面拼接set,suffixOverrides去除多余的逗号-->
<trim prefix="set" suffixOverrides=",">
<if test="user.username != null">
username = #{user.username},
</if>
<if test="user.password != null">
password = #{user.password},
</if>
<if test="user.age != null and user.age != 0">
age = #{user.age},
</if>
<if test="user.gender != null">
gender = #{user.gender}
</if>
</trim>
<where>
id = #{user.id}
</where>
</update>
测试方法
@Test
public void mytest5(){
User user = new User();
user.setAge(20);
//user.setGender("female");
user.setId(2);
int update = mapper.updateSelectUser(user);
logger.info("update = " + update);
}
7.5 set标签
使用set标签对上面的实现进行一定改进。
接口UserMapper
int updateSelectUserSet(@Param("user") User user);
UserMapper.xml
<!--set标签 :会自动拼接set同时去除逗号-->
<update id="updateSelectUserSet">
update j23_user_t
<set>
<if test="user.username != null">
username = #{user.username},
</if>
<if test="user.password != null">
password = #{user.password},
</if>
<if test="user.age != null and user.age != 0">
age = #{user.age},
</if>
<if test="user.gender != null">
gender = #{user.gender}
</if>
</set>
<where>
id = #{user.id}
</where>
</update>
测试方法
@Test
public void mytest6(){
User user = new User();
user.setAge(20);
//user.setGender("female");
user.setId(2);
int update = mapper.updateSelectUserSet(user);
logger.info("update = " + update);
}
7.6 sql-include标签
sql标签定义一个sql片段,include标签引用sql标签里的内容。
作用:将一部分重复的sql语句提取出去,来进行引用。
<!--用法1:sql片段-->
<sql id="baseSelectUser">
select id,username,password,age,gender,update_date from j23_user_t
</sql>
<!--用法2:sql片段里面可以包括其他标签-->
<sql id="where_id">
<where>
id = #{id}
</where>
</sql>
<!--引用sql片段-->
<select id="selectUserById" resultMap="userMap">
<include refid="baseSelectUser"/>
<include refid="where_id"/>
</select>
<!--用法3:把表的列放到sql标签中-->
<sql id="base_column_list">
id,username,password,age,gender,update_date
</sql>
<!--引用sql片段作为sql语句中表的列-->
<select id="selectUserByUsernameAndPassword" resultMap="userMap">
select <include refid="base_column_list"/> from j23_user_t
<where>
<if test="username != null">
username = #{username}
</if>
<if test="password != null">
and password = #{password}
</if>
</where>
</select>
7.7 selectKey标签
insert时使用
7.7.1 order为AFTER
举个例子:
有个user对象插入到数据库,id是自增,插入到数据库后获得id。
接口UserMapper
//selectKey
int insertUser(@Param("user") User user);
UserMapper.xml
<!--selectKey → 查询结果封装给输入映射传入的值-->
<!--
order:selectKey中的语句执行顺序 → 相对于insert语句的
keyColumn:selectKey查询语句中查询结果的列名
keyProperty:查询结果要封装给谁 → 通常写的是和输入映射的值相关 → @Param注解的value属性值相关
resultType:keyProperty的类型
-->
<insert id="insertUser">
<selectKey order="AFTER" keyColumn="increasedId" keyProperty="user.id" resultType="int">
select LAST_INSERT_ID() as increasedId
</selectKey>
insert into j23_user_t
(username,password,age,gender,update_date)
values(#{user.username},#{user.password},#{user.age},#{user.gender},#{user.updateDate})
</insert>
测试方法
@Test
public void mytest7(){
User user = new User();
user.setUsername("zzz");
user.setPassword("789");
user.setAge(20);
user.setGender("female");
user.setUpdateDate(new Date());
int insert = mapper.insertUser(user);
logger.info("insert = " + insert);
logger.info("user = " + user);
}
7.7.2 order为BEFORE
场景:插入一个user数据,username是随机生成的UUID → uuid生成在执行insert之前
接口UserMapper
int insertUserOrderBefore(@Param("user") User user);
UserMapper.xml
<insert id="insertUserOrderBefore">
<!--获取作为username的uuid是在insert操作之前获取的-->
<selectKey order="BEFORE" keyColumn="username" keyProperty="user.username" resultType="string">
select uuid() as username
</selectKey>
insert into j23_user_t
(username,password,age,gender,update_date)
values (#{user.username},#{user.password},#{user.age},#{user.gender},#{user.updateDate})
</insert>
测试方法
@Test
public void mytest8(){
User user = new User();
//user.setUsername("zzz");
user.setPassword("789");
user.setAge(20);
user.setGender("female");
user.setUpdateDate(new Date());
int insert = mapper.insertUserOrderBefore(user);
logger.info("insert = " + insert);
logger.info("user = " + user);
}
7.8 insert标签中的属性
通过insert标签中的useGeneratedKeys属性获取自增主键。
接口UserMapper
//使用insert标签的属性 获得自增的主键
int insertUserGenerateKey(@Param("user") User user);
UserMapper.xml
<!--自增主键-->
<insert id="insertUserGenerateKey" useGeneratedKeys="true" keyProperty="user.id">
insert into j23_user_t
(username,password,age,gender,update_date)
values (#{user.username},#{user.password},#{user.age},#{user.gender},#{user.updateDate})
</insert>
测试方法
@Test
public void mytest9(){
User user = new User();
user.setUsername("ali");
user.setPassword("789");
user.setAge(20);
user.setGender("female");
user.setUpdateDate(new Date());
int insert = mapper.insertUserGenerateKey(user);
logger.info("insert = " + insert);
logger.info("user = " + user);
}
7.9 foreach标签
和foreach语句做一个类比,帮助理解。
接口UserMapper
//foreach
List<User> selectUserByIdInArray(Integer[] ids); //collection = array
List<User> selectUserByIdInList(List<Integer> ids); //collection = list
List<User> selectUserByIdInArrayParam(@Param("ids") Integer[] ids); //collection = ids
List<User> selectUserByIdInListParam(@Param("ids") List<Integer> ids); // collection = ids
UserMapper.xml
<!--foreach-->
<sql id="base_select">
select id,username,password,age,gender,update_date from j23_user_t
</sql>
<!--collection:填的值和输入映射相关
数组:array
list:list
@Param:value属性值
separator:foreach标签里的内容每两个之间的分隔符
open和close:整个foreach执行完成最前和最后的补充内容
-->
<select id="selectUserByIdInArray" resultMap="userMap">
<include refid="base_select"/>
<where>
id in
<foreach collection="array" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</where>
</select>
<select id="selectUserByIdInList" resultMap="userMap">
<include refid="base_select"/>
<where>
id in
<foreach collection="list" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</where>
</select>
<select id="selectUserByIdInArrayParam" resultMap="userMap">
<include refid="base_select"/>
<where>
id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</where>
</select>
<select id="selectUserByIdInListParam" resultMap="userMap">
<include refid="base_select"/>
<where>
id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</where>
</select>
测试方法
//foreach
@Test
public void mytest11(){
Integer[] ids = {1, 2, 3, 4, 5};
List<User> users = mapper.selectUserByIdInArray(ids);
for (User user : users) {
logger.info(user);
}
}
7.9.1insert多条记录
接口UserMapper
//foreach → 批量插入多条记录
int insertUsers(@Param("users") List<User> users);
UserMapper.xml
<insert id="insertUsers" useGeneratedKeys="true" keyProperty="users.id">
insert into j23_user_t (username,password,age,gender,update_date)
values
<foreach collection="users" item="user" separator=",">
(#{user.username},#{user.password},#{user.age},#{user.gender},#{user.updateDate})
</foreach>
</insert>
测试方法
@Test
public void mytest12(){
User user1 = new User(null, "user1", "pwd1", 21, "male",new Date());
User user2 = new User(null, "user2", "pwd2", 22, "male",new Date());
User user3 = new User(null, "user3", "pwd3", 23, "male",new Date());
User user4 = new User(null, "user4", "pwd4", 24, "male",new Date());
ArrayList<User> users = new ArrayList<>();
users.add(user1);
users.add(user2);
users.add(user3);
users.add(user4);
int i = mapper.insertUsers(users);
System.out.println("insert = " + i);
}
八、多表查询
8.1 一对一
以一个用户(User)对应一张用户详情表(UserDetail)为例查询。
8.1.1数据库表关系
8.1.2 javabean关系
8.1.3 查询
8.1.3.1 分次查询
接口UserMapper
//根据username查询user信息 → user需要绑定对应的UserDetail信息
User selectUserByName(@Param("username") String username);
UserMapper.xml
<resultMap id="userMap" type="com.cskaoyan.bean.User">
<result property="id" column="id"/>
<result property="username" column="username"/>
<result property="password" column="password"/>
<result property="age" column="age"/>
<result property="gender" column="gender"/>
<result property="updateDate" column="update_date"/>
<!--
column: 查询结果的列名 → 并且为第二次查询提供参数
property: 始终对应父标签type的javabean的成员变量名
select: 第二次查询的 命名空间+id
-->
<association property="userDetail" column="id"
select="com.cskaoyan.mapper.UserMapper.selectUserDetailByUid"/>
</resultMap>
<select id="selectUserByName" resultMap="userMap">
select id,username,password,age,gender,update_date from j23_user_t where username = #{username}
</select>
<!--第二次查询#{}里的值如何写 → 随便写-->
<select id="selectUserDetailByUid" resultType="com.cskaoyan.bean.UserDetail">
select id,phone,email from j23_user_detail_t where user_id = #{userId}
</select>
测试方法
@Test
public void mytest1(){
User user = mapper.selectUserByName("laotan");
System.out.println(user);
}
8.1.3.2 连接查询
通常使用的是左连接查询 能够保证左表的信息的完整进行封装 ,右表中的字段允许为null。
连接查询是一次性将信息都查询出来,然后再进行封装。
接口UserMapper
User selectUserByNameLeft(@Param("username") String username);
UserMapper.xml
<!--
不管使用的是哪里的
property: 始终对应的是其父标签类型的javabean成员变量名
column: 始终对应的查询结果的列名
-->
<resultMap id="userMapLeft" type="com.cskaoyan.bean.User">
<result property="id" column="id"/>
<result property="username" column="username"/>
<result property="password" column="password"/>
<result property="age" column="age"/>
<result property="gender" column="gender"/>
<result property="updateDate" column="update_date"/>
<association property="userDetail" javaType="com.cskaoyan.bean.UserDetail">
<result property="id" column="udid"/>
<result property="phone" column="phone"/>
<result property="email" column="email"/>
</association>
</resultMap>
<!--连接查询过程中查询结果单额列名出现重复 → 通常使用as起个别名-->
<select id="selectUserByNameLeft" resultMap="userMapLeft">
select
u.id,u.username,u.password,u.age,u.gender,u.update_date,
ud.id as udid,ud.phone,ud.email
from j23_user_t u
LEFT JOIN j23_user_detail_t ud on u.id = ud.user_id
where u.username = #{username}
</select>
测试方法
@Test
public void mytest2(){
User user = mapper.selectUserByNameLeft("laotan");
logger.info(user);
}
8.2 一对多
以一个用户(User)可以对应多个订单信息(Order)为例根据用户名查询用户信息和相对应的用户订单信息。
8.2.1数据库表关系
8.2.2javabean关系
8.2.3查询
8.2.3.1 分次查询
接口UserMapper
User selectUserByNameOrder(@Param("username") String username);
UserMapper.xml
<resultMap id="userMap" type="com.qyt.bean.User">
<result property="id" column="id"/>
<result property="username" column="username"/>
<result property="password" column="password"/>
<result property="age" column="age"/>
<result property="gender" column="gender"/>
<result property="updateDate" column="update_date"/>
<collection property="orders" column="id"
select="com.qyt.mapper.UserMapper.selectOrderByUid"/>
</resultMap>
<select id="selectUserByNameOrder" resultMap="userMap">
select id,username,password,age,gender,update_date from j23_user_t where username = #{username};
</select>
<resultMap id="orderMap" type="com.qyt.bean.Order">
<result property="id" column="id"/>
<result property="ordername" column="ordername"/>
<result property="money" column="money"/>
</resultMap>
<select id="selectOrderByUid" resultMap="orderMap">
SELECT id,ordername,money from j23_order_t where user_id =#{user_id};
</select>
测试方法
@Test
public void mytest1() {
User user = mapper.selectUserByNameOrder("laotan");
logger.info(user);
}
8.2.3.2连接查询
接口UserMapper
User selectUserByNameOrderLeft(@Param("username") String username);
UserMapper.xml
<resultMap id="userMapLeft" type="com.qyt.bean.User">
<result property="id" column="id"/>
<result property="username" column="username"/>
<result property="password" column="password"/>
<result property="age" column="age"/>
<result property="gender" column="gender"/>
<result property="updateDate" column="update_date"/>
<collection property="orders" ofType="com.qyt.bean.Order">
<result property="id" column="oid"/>
<result property="ordername" column="ordername"/>
<result property="money" column="money"/>
</collection>
</resultMap>
<select id="selectUserByNameOrderLeft" resultMap="userMapLeft">
SELECT u.id,u.username,u.password,u.age,u.gender,u.update_date,
o.id as oid,o.ordername,o.money
from j23_user_t u
LEFT JOIN j23_order_t o on u.id = o.user_id
where u.username = #{username}
</select>
测试方法
@Test
public void mytest2() {
User user = mapper.selectUserByNameOrderLeft("laotan");
logger.info(user);
}
8.3 多对多
以学生和课程的多对多关系为例,一个学生可以选修多门课程,一个课程可以有多个学生学习。场景:根据学生姓名查询学生信息及学生的选课信息,如果username(用户名)为null,则查询所有学生信息。
8.3.1数据库表关系
8.3.2javabean关系
8.3.3查询
8.3.3.1 分次查询
接口UserMapper
List<Student> selectStudentByName(@Param("studentName") String studentName);
UserMapper.xml
<!--返回值封装成map-->
<resultMap id="studentMap" type="com.qyt.bean.Student">
<result property="id" column="id"/>
<result property="studentName" column="student_name"/>
<collection property="courses" column="id"
select="com.qyt.mapper.StudentMapper.selectCourseByStudentId"/>
</resultMap>
<!--第一次查询-->
<select id="selectStudentByName" resultMap="studentMap">
select id,student_name from j23_student_t
<!--引用sql片段-->
<include refid="selective_student_name"/>
</select>
<!--第二次查询,根据student_id对课程信息表和中间关系表进行连接查询,查询学生的所有课程信息-->
<select id="selectCourseByStudentId" resultType="com.qyt.bean.Course">
select c.id,c.course_name as courseName
from j23_course_t c
LEFT JOIN j23_relationship_t r on c.id = r.course_id
where student_id = #{stuid}
</select>
测试方法
@Test
public void test1(){
List<Student> students = studentMapper.selectStudentByName("laotan");
logger.info(students);
}
@Test
public void test2(){
List<Student> students = studentMapper.selectStudentByName(null);
logger.info(students);
}
8.3.3.2 连接查询
接口UserMapper
List<Student> selectStudentByNameLeft(@Param("studentName") String studentName);
UserMapper.xml
<sql id="selective_student_name">
<where>
<if test="studentName != null">
student_name = #{studentName}
</if>
</where>
</sql>
<resultMap id="studentMapLeft" type="com.qyt.bean.Student">
<result property="id" column="id"/>
<result property="studentName" column="student_name"/>
<collection property="courses" ofType="com.qyt.bean.Course">
<result property="id" column="cid"/>
<result property="courseName" column="course_name"/>
</collection>
</resultMap>
<select id="selectStudentByNameLeft" resultMap="studentMapLeft">
SELECT
s.id,
s.student_name,
c.id as cid,
c.course_name
from j23_student_t s
LEFT JOIN j23_relationship_t r on s.id = r.student_id
LEFT JOIN j23_course_t c on c.id = r.course_id
<include refid="selective_student_name"/>
</select>
测试方法
@Test
public void test3(){
List<Student> students = studentMapper.selectStudentByNameLeft("laotan");
logger.info(students);
}
@Test
public void test4(){
List<Student> students = studentMapper.selectStudentByNameLeft(null);
logger.info(students);
}