一、基于代理DAO实现CRUD操作
1.1 根据ID查询
1.1.1 持久层接口 IUserDao
/**
* 根据ID查用户
* @param userId
* @return
*/
User findById(Integer userId);
1.1.2 映射配置文件 IUserDao.xml
resultType
:用于指定结果集的类型parameterType
:用于指定传入参数的类型sql语句中的
#{}
:代表占位符,相当于原先JDBC中的?,用于执行语句时替换实际的数据。- 数据类型为基本类型可随意填写
<!-- 根据ID查用户-->
<select id="findById" parameterType="int" resultType="com.ruki.domain.User">
select * from user where id=#{id};
</select>
1.2 保存操作
1.2.1 持久层接口 IUserDao
/**
* 保存用户
* @param user
*/
void saveUser(User user);
1.2.2 映射配置文件 IUserDao.xml
parameterType
:代表参数的类型,因为要传入的是一个类的对象,所以类型就写类的全名称#{}
:参数占位符- 由于保存方法的参数是一个
User
对象,此处要写User
对象中的属性名称,因此采用OGNL表达式。 OGNL表达式
- 是APACHE提供的一种表达式语言
- Object Graphic Navigation Language 对象图导航语言
- 它是按照一定的语法格式来获取数据的
#{对象.对象}
#{user.username}
它会先去找user
对象,然后在user
对象中找到username
属性,并调用getUsername()
方法把值取出来。但由于在parameterType
属性上指定了实体类名称,所以可以省略user.
,而直接写username
- 由于保存方法的参数是一个
selectKey
keyProperty
:实体类中主键的属性名keyColumn
:表中主键的属性名resultType
:结果类型order
:执行顺序
<!-- 保存用户 -->
<insert id="saveUser" parameterType="com.ruki.domain.User">
<!-- 配置保存时获取插入的id -->
<selectKey keyProperty="id" keyColumn="id" resultType="int" order="AFTER">
select last_insert_id();
</selectKey>
insert into user (username, address, sex, birthday) values (#{username}, #{address}, #{sex}, #{birthday})
</insert>
1.3 用户更新
1.3.1 持久层接口 IUserDao
/**
* 修改用户信息
* @param user
*/
void update(User user);
1.3.2 映射配置文件 IUserDao.xml
<!-- 修改用户信息-->
<update id="update" parameterType="com.ruki.domain.User">
update user set username=#{username},address=#{address},sex=#{sex},birthday=#{birthday} where id=#{id}
</update>
1.4 用户删除
1.4.1 持久层接口 IUserDao
/**
* 删除用户
* @param userId
*/
void deleteUser(Integer userId);
1.4.2 映射配置文件 IUserDao.xml
<!-- 根据id删除用户
参数类型:int INT INTEGER Integer都行-->
<delete id="deleteUser" parameterType="integer">
delete from user where id=#{id}
</delete>
1.5 用户模糊查询
1.5.1 持久层接口 IUserDao
/**
* 根据名字模糊查询
* @param name
* @return
*/
List<User> findByName(String name);
1.5.2 映射配置文件 IUserDao.xml
模糊查询第一种配置方式
select * from USER where username like #{name}
- 传入的参数需要自己带%,使用的是
preparedStatement
预编译,推荐使用
模糊查询第二种配置方式
select * from user where username like '%${value}%'
- 传入参数不用带%,使用的是字符串拼接,不推荐
<!-- 根据用户名模糊查询 -->
<select id="findByName" parameterType="String" resultType="com.ruki.domain.User">
<!-- preparedStatement预处理了,推荐使用 -->
<!--select * from USER where username like #{name} -->
<!-- 字符串的拼接,不推荐 -->
select * from user where username like '%${value}%'
</select>
1.5.3 #{}与${}的区别
#{}
表示一个占位符号- 通过
#{}
可以实现preparedStatement
向占位符中设置值,自动进行java类型和Jdbc类型转换,#{}
可以有效防止sql注入。#{}
可以接收简单类型或pojo属性值。如果parameterType
传输单个简单类型值,#{}
括号中可以是value
或其他名称
- 通过
${}
表示拼接sql串- 通过
${}
可以将parameterType
传入的内容拼接在sql中且不进行Jdbc转换,${}
可以接收简单类型值或pojo属性值,如果patameterType
传输单个简单类型值,${}
括号中只能是value
- 通过
1.6 查询使用聚合函数
1.6.1 持久层接口 IUserDao
/**
* 查询一行一列的结果,聚合函数
* @return
*/
int findTotalCount();
1.6.2 映射配置文件 IUserDao.xml
<!-- 查询总记录数 -->
<select id="findTotalCount" resultType="int">
select count(id) from user
</select>
1.7 测试
package com.ruki.test;
import com.ruki.dao.IUserDao;
import com.ruki.domain.QueryVo;
import com.ruki.domain.User;
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.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.List;
public class MybatisTest {
private InputStream in;
private SqlSession session;
private IUserDao userDao;
@Before
public void init() throws IOException {
in = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory sessionFactory = builder.build(in);
session = sessionFactory.openSession();
userDao = session.getMapper(IUserDao.class);
}
@After
public void destroy() throws IOException {
session.commit();//提交事务
session.close();
in.close();
}
/**
* 测试查询所有用户
*/
@Test
public void testFindAll() throws IOException {
List<User> list = userDao.findAll();
for(User user : list){
System.out.println(user);
}
}
/**
* 测试保存用户
*/
@Test
public void testSaveUser(){
User user = new User();
user.setUsername("test02 last_insert_id");
user.setAddress("浙江杭州市");
user.setSex("男");
user.setBirthday(new Date());
System.out.println("保存前:" + user);
userDao.saveUser(user);
System.out.println("保存后:" + user);
}
/**
* 测试更新用户信息
*/
@Test
public void testUpdate(){
User user = new User();
user.setId(50);
user.setUsername("test01 update");
user.setSex("女");
userDao.update(user);
}
/**
* 测试删除用户
*/
@Test
public void testDelete(){
userDao.deleteUser(48);
}
/**
* 根据ID查用户
*/
@Test
public void testFindById(){
User user = userDao.findById(45);
System.out.println(user);
}
/**
* 测试根据用户名模糊查询
*/
@Test
public void testFindByName(){
// List<User> users = userDao.findByName("%王%");
List<User> users = userDao.findByName("王");
for(User user : users){
System.out.println(user);
}
}
/**
* 查询总记录数 - 聚合函数
*/
@Test
public void testGetTotalCount(){
int totalCount = userDao.findTotalCount();
System.out.println(totalCount);
}
}
二、MyBatis参数的深入
2.1 patameterType配置参数
2.1.1 使用说明
- sql语句传参,使用标签的
parameterType
属性来设定,该属性的取值可以是基本类型,引用类型(String
),还可以是实体类类型(pojo),同时也可以使用实体类的包装类。
2.1.2 注意事项
- 基本类型和
String
,可以直接写类型名称,也可以使用包名.类名
的方式,如java.lang.String
- 实体类类型,在不手动注册别名的情况下,只能使用全限定类名。
- MyBatis在加载时已经把常用的数据类型注册了别名,而使用自己写的实体类没有注册,所以必须写全限定类名。
2.2 传递pojo包装对象
2.2.1 编写QueryVo
package com.ruki.domain;
public class QueryVo {
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
2.2.2 编写持久层接口
/**
* 根据实体类的包装对象进行模糊查询
* @param vo
* @return
*/
List<User> findByVo(QueryVo vo);
2.2.3 持久层接口的映射文件
<!-- 根据实体类的包装对象进行模糊查询 -->
<select id="findByVo" parameterType="com.ruki.domain.QueryVo" resultType="com.ruki.domain.User">
select * from user where username like #{user.username};
</select>
2.2.4 测试
/**
* 将查询条件包装成实体类的对象,
* 并将其作为参数传入,
* 使用OGNL表达式获得对象中的具体参数
*/
@Test
public void testFindUserByVo(){
QueryVo vo = new QueryVo();
User user = new User();
user.setUsername("%王%");
vo.setUser(user);
List<User> users = userDao.findByVo(vo);
for(User u : users){
System.out.println(u);
}
}
三、MyBatis的输出结果封装
3.1 resultType配置结果类型
resultType
和parameterType
一样,如果注册过别名可以直接使用别名,没有注册过,只能使用全限定类名。- 实体类中属性名称必须和查询语句汇总的列名保持一致,否则无法实现封装。
3.2 修改实体类属性名
- 此时实体类属性名和表中字段名不一致
3.2.1 实体类
public class User implements Serializable {
private Integer userId;
private String userName;
private Date userBirthday;
private String userSex;
private String userAddress;
//..getter and setter
}
从查询结果可以发现,只有
userName
被封装了起来,其余都为null
- 原因:mysql在window中不区分大小写,因此和表中字段名一致。
3.2.2 解决方式1:修改映射配置(起别名)
<!-- 配置查询所有操作 -->
<select id="findAll" resultType="com.itheima.domain.User">
select id as userId,username as userName,birthday as userBirthday, sex as userSex,address as userAddress from user
</select>
- 缺陷:若查询很多或属性很多,使用别名会很麻烦。
3.2. 解决方式2:resultMap
id
标签:用于指定主键字段result
标签:用户指定非主键字段colomn
属性:用于指定数据库列名property
属性:用于指定实体类属性名称
<!-- 建立User实体和数据库表的对应关系
type属性:指定实体类的全限定类名
id属性:给定一个唯一标识,是给查询select标签引用用的。 -->
<resultMap type="com.itheima.domain.User" id="userMap">
<id column="id" property="userId"/>
<result column="username" property="userName"/>
<result column="sex" property="userSex"/>
<result column="address" property="userAddress"/>
<result column="birthday" property="userBirthday"/>
</resultMap>
<!-- 配置查询所有操作 -->
<select id="findAll" resultMap="userMap">
select * from user
</select>
四、SqlMapConfig.xml
4.1 配置内容
4.1.1 配置内容和顺序
properties
:属性property
settings
:全局配置参数setting
typeAliases
:类型别名typeAliase
package
typeHandlers
:类型处理器objectFactory
:对象工厂plugins
:插件environments
:环境集合属性对象environment
:环境子属性对象transactionManager
:事务管理dataSource
:数据源
mappers
:映射器mapper
package
4.2 properties(属性)
4.2.1 方式1
<properties>
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis01"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</properties>
4.2.2 方式2
4.2.2.1 在classpath下定义db.properties
db.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis01
jdbc.username=root
jdbc.password=root
4.2.2.2 properties标签配置
<!-- 配置properties
可以在标签内部配置连接数据库的信息,也可以通过属性引用外部配置文件信息
resource属性:
用于指定配置文件的位置,是按照类路径的写法来写,并且必须存在于类路径下
url属性:
按照url的写法来写地址
URL:uniform resource locator 统一资源定位符。可以唯一标识一个资源的位置
写法:
http://localhost:8080/mybatis/demo1Servlet
协议 主机 端口 URI
URI:uniform resource identifier 统一资源标识符。在应用中可以唯一定位
-->
<!--<properties resource="jdbcConfig.properties"></properties>-->
<properties url="file:///D:/IDEAworkspace/day02_eesy_01mybatisCRUD/src/main/resources/jdbcConfig.properties"></properties>
4.2.2.3 dataSource修改
<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>
4.3 typeAliases(类型别名)
- 自定义别名
typeAliases
:用于配置别名type
属性:实体类全限定类名alias
属性:指定别名。<font color="red">不区分大小写</font>package
标签:指定要配置别名的包。<font color="red">别名就是类名</font>
<typeAliases>
<!-- typeAlias用于配置别名。
type属性:
指定的是实体类全限定类名。
alias属性:
指定别名。指定了别名就不再区分大小写
-->
<!-- 单个别名定义 -->
<<typeAlias type="com.ruki.domain.User" alias="user"></typeAlias>
<!-- 批量别名定义,扫描整个包下的类,别名为类名(不区分大小写) -->
<!-- 用于指定要配置别名的包当指定之后,该包下的实体类都会注册别名
并且类名就是别名,不再区分大小写-->
<package name="com.ruki.domain"/>
</typeAliases>
4.4 mappers(映射器)
4.4.1 <mapper resource="" />(基于xml配置文件)
使用相对于类路径的资源
<mapper resource="com/ruki/dao/IUserDao.xml" />
4.4.2 <mapper class="" />(基于注解)
使用mapper或者说是dao接口类路径
<mapper class="com.ruki.dao.IUserDao"/>
4.4.3 <package name="" />
注册指定包下的所有mapper或者说是dao接口
<mappers> <!-- package标签用于指定dao接口所在的包,当制定了之后就不需要再写mapper以及resource(xml配置文件形式)或者class(注解形式)了 --> <package name="com.ruki.dao"/> </mappers>
- <font color="red">此种方法要求mapper或者说dao接口名称和mapper映射文件名称相同,且放在同一个目录中</font>