基本的CRUD操作
- 步骤:在dao接口编写方法 --> 在映射配置文件配置方法sql语句 --> 在测试类调用dao方法。
/**
* 用户的持久层接口
*/
public interface IUserDao {
//查询所有用户
List<User> findAll();
//保存用户
void saveUser(User user);
//更新用户
void updateUser(User user);
//根据id删除用户
void deleteUser(Integer id);
//根据id查询单个用户
User findById(Integer id);
//模糊查询
List<User> findByName(String username);
//查询总用户数 -- 聚合函数
int findTotal();
}
//IUserDao.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">
<!--类所在 方法id 封装类型-->
<mapper namespace="com.itheima.dao.IUserDao">
<!--查询所有用户-->
<select id="findAll" resultType="com.itheima.domain.User">
select * from user;
</select>
<!--保存用户-->
<!--parameterType:告知要传入的参数类型-->
<insert id="saveUser" parameterType="com.itheima.domain.User">
<!--配置插入操作后,获取插入数据的id-->
<!--keyProperty:实体类属性 keyColumn:数据库字段 order:什么时候执行-->
<selectKey keyProperty="id" keyColumn="id" resultType="int" order="AFTER">
<!--可以知道插入的id-->
select last_insert_id();
</selectKey>
insert into user(username,address,sex,birthday)values(#{username},#{address},#{sex},#{birthday});
</insert>
<!--更新用户-->
<update id="updateUser" parameterType="com.itheima.domain.User">
update user set username=#{username},address=#{address},sex=#{sex},birthday=#{birthday} where id=#{id};
</update>
<!--删除用户-->
<delete id="deleteUser" parameterType="Integer">
delete from user where id=#{uid};
</delete>
<!--根据id查询用户-->
<!--查询返回的是一个user,所以需要设置resultType-->
<select id="findById" parameterType="Integer" resultType="com.itheima.domain.User">
select * from user where id=#{uid};
</select>
<!--根据名称模糊查询用户-->
<select id="findByName" parameterType="String" resultType="com.itheima.domain.User">
select * from user where username like #{uname}
<!--select * from user where username like '%${value}%' 这种直接拼接字符串的少用-->
</select>
<!--查询总用户数-->
<select id="findTotal" resultType="int">
select count(id) from user;
</select>
</mapper>
/**
* 测试mybatis的CRUD操作
*/
public class MybatisTest {
private InputStream in;
private SqlSession sqlSession;
private IUserDao dao;
//提取重复代码
@Before //用于在测试方法执行之前执行
public void init() throws Exception {
//1 读取配置文件,生成字节输入流
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2 获取SqlSessionFactory对象
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//3 获取SqlSession对象
sqlSession = factory.openSession();
//4 获取dao代理对象
dao = sqlSession.getMapper(IUserDao.class);
}
//释放资源
@After //用于在测试方法之后执行
public void destroy() throws Exception {
//提交事务
sqlSession.commit(); //执行增删改时会自动提交
//6 释放资源
sqlSession.close();
in.close();
}
//测试查询所有
@Test
public void testFindAll() throws Exception {
//5 执行查询所有
List<User> users = dao.findAll();
for (User user : users) {
System.out.println(user);
}
}
//测试保存用户
@Test
public void testSave() throws IOException {
User user = new User();
user.setUsername("insert id");
user.setAddress("北京朝阳");
user.setSex("男");
user.setBirthday(new Date());
System.out.println("保存操作之前:"+user); //id=null
//执行保存
dao.saveUser(user);
System.out.println("保存操作之后:"+user); //可以获取id
}
//测试更新用户
@Test
public void updateUser() throws IOException {
User user = new User();
user.setId(41);
user.setUsername("mybatis updateUser");
user.setAddress("北京朝阳");
user.setSex("女");
user.setBirthday(new Date());
//执行更新
dao.updateUser(user);
}
//测试删除用户
@Test
public void testDelete() throws IOException {
//执行删除
dao.deleteUser(46);
}
//测试根据id查询用户
@Test
public void testFindOne() throws IOException {
//执行查询
User user = dao.findById(48);
System.out.println(user);
}
//测试根据名称模糊查询用户
@Test
public void testFindByName() throws IOException {
//执行查询
List<User> users = dao.findByName("%王%");
// List<User> users = dao.findByName("王");
for (User user : users) {
System.out.println(user);
}
}
//测试查询总用户数
@Test
public void testFindTotal(){
//执行查询
int count = dao.findTotal();
System.out.println("总记录数"+count);
}
}
Mybatis的参数
ParameterType(输入类型)
使用标签的 parameterType 属性来设定。该属性的取值可以
是基本类型,引用类型(例如:String 类型),还可以是实体类类型(POJO 类)。同时也可以使用实体类的包装类,本章节将介绍如何使用实体类的包装类作为参数传递。
2.2 传递简单类型
2.3 传递pojo对象
- Mybatis使用ongl表达式解析对象字段的值,#{}或者${}括号中的值为pojo属性名称。
- OGNL表达式:
Object | Graphic | Navigation | Language |
---|---|---|---|
对象 | 图 | 导航 | 语言 |
它是通过对象的取值方法来获取数据,在写法上把get给省略了。
eg:类中的写法:user.getUsername();
ONGL表达式的写法:user.username
mybatis中可以直接写username而不用user. 因为在parameterType中已经提供了属性所属的类,所以此时不需要写对象名。
2.4 传递pojo包装对象
开发中通过pojo传递查询条件,查询条件是综合的条件,不仅包括用户查询条件还包括其他的查询条件,这时可以使用包装对象传递参数。Pojo类中包含pojo.
eg:根据用户名查询用户信息,查询条件放到QueryVo的user属性中
public class QueryVo {
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
<!--根据QueryVo的条件查询用户-->
<select id="findUserByVo" parameterType="com.itheima.domain.QueryVo" resultType="com.itheima.domain.User">
select * from user where username like #{user.username}
</select>
//测试使用QueryVo作为查询条件
@Test
public void testFindByVo() throws IOException {
//执行查询
QueryVo vo = new QueryVo();
User user = new User();
user.setUsername("%王%");
vo.setUser(user);
List<User> users = dao.findUserByVo(vo);
for (User u : users) {
System.out.println(u);
}
}
resultType(输出类型)
resultType 属性可以指定结果集的类型,它支持基本类型和实体类类型。
它和 parameterType 一样,如果注册过类型别名的,可以直接使用别名。没有注册过的必须使用全限定类名。例如:我们的实体类此时必须是全限定类名
同时,实体类中的属性名称必须和查询语句中的列名保持一致,否则无法
实现封装。
如果修改了实体类中属性名称,与数据库字段名称不对应,那么应该在配置文件中设置别名或者配置resultMap对应。
//更改实体类属性名称
public class User implements Serializable {
private Integer userId;
private String userName;
private Date userBirthday;
private String userSex;
private String userAddress;
...
}
- 第一种: 配置别名
//使用别名查询
<!-- 配置查询所有操作 -->
<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>
- 第二种:配置resultMap
resultMap 标签可以建立查询的列名和实体类的属性名称不一致时建立对应关系。从而实现封装。
在 select 标签中使用 resultMap 属性指定引用即可。同时 resultMap 可以实现将查询结果映射为复杂类
型的 pojo,比如在查询结果映射对象中包括 pojo 和 list 实现一对一查询和一对多查询。
<!-- 建立 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>
id 标签:用于指定主键字段
result 标签:用于指定非主键字段
column 属性:用于指定数据库列名
property 属性:用于指定实体类属性名称
//传递resultMap的id
<!-- 配置查询所有操作 -->
<select id="findAll" resultMap="userMap">
select * from user
</select>
但是建议还是将实体类属性名称与数据库字段名称对应,可以省去一些麻烦。
Mybatis实现传统DAO层开发[了解]
mybatis会帮我们找实现类的实现方法,所以其实没有必要按照传统的方式写,建议多了解源码,或许会明白这两种方式的区别。
//许多代码都行类似的,session调用不同的方法实现不同的功能
public class UserDaoImpl implements IUserDao {
private SqlSessionFactory factory;
//覆盖构造方法
public UserDaoImpl(SqlSessionFactory factory){
this.factory = factory;
}
public List<User> findAll() {
//1 根据factory获取SqlSession对象
SqlSession session = factory.openSession();
//2 调用SqlSession中的方法,实现查询列表
List<User> users = session.selectList("com.itheima.dao.IUserDao.findAll");//参数是能获取配置信息的key
//3 释放资源
session.close();
return users;
}
...
)
*/
public class MybatisTest {
private InputStream in;
private IUserDao userdao;
//提取重复代码
@Before //用于在测试方法执行之前执行
public void init() throws Exception {
//1 读取配置文件,生成字节输入流
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2 获取SqlSessionFactory对象
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//3 使用工厂对象,创建dao对象
userdao = new UserDaoImpl(factory); //不再是创建代理对象,而是直接new实现类
}
...
}
properties(属性)
- 在使用 properties 标签配置时,我们可以采用两种方式指定属性配置。
- 将配置信息放在properties中,在下面的dataSource 里引用,这样看上去有点多此一举,但是其实properties支持我们把里面的信息放到一个外部的配置文件。
<properties>
<property name="driver" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/eesy_mybatis?characterEncoding=utf8"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</properties>
<!--配置环境-->
<environments default="mysql">
<!--配置mysql的环境-->
<environment id="mysql">
<!--配置事务-->
<transactionManager type="JDBC"></transactionManager>
<!--配置连接池-->
<dataSource type="POOLED">
<property name="driver" value="${driver}"></property>
<property name="url" value="${url}"></property>
<property name="username" value="${username}"></property>
<property name="password" value="${password}"></property>
</dataSource>
</environment>
</environments>
- 将数据库连接信息写在外部的配置文件
//jdbcConfig.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/eesy_mybatis?characterEncoding=utf8
jdbc.username=root
jdbc.password=root
<!--配置properties,可以在标签内部配置连接数据库的信息,也可以通过属性引用外部配置文件信息
resource属性:
用于指定配置文件的位置,是按照类路径的写法来写,而且必须存在于类路径下
url属性:
要求按照url的写法来写地址
URL:统一资源定位符,它可以唯一标识一个资源位置
写法:
URL: Uniform Resource Locator 统一资源定位符
http://localhost:8080/mystroe/CategoryServlet URL
协议 主机 端口 URI
URI:Uniform Resource Identifier 统一资源标识符
/mystroe/CategoryServlet
它是可以在 web 应用中唯一定位一个资源的路径
-->
<properties resource="jdbcConfig.properties"></properties>
//也可以这么写
<properties url="file:///D:/JavaCode/mybatis/day02_01mybatisCRUD/src/main/resources/jdbcConfig.properties"></properties>
...
//dataSource 里面的property 就变成上面配合的引用
<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>
typeAliases(类型别名)
Mybatis 支持的默认别名,我们也可以自定义别名
<!--使用typeAliases配置别名,它只能配置domain类中的别名-->
<typeAliases>
<!--typeAlias用于配置别名,type指定的是实体类全限定类名,alias指定别名,当指定了别名就不再区分大小写-->
<typeAlias type="com.itheima.domain.User" alias="user"></typeAlias>
</typeAliases>
<typeAliases>
<!--多实现类配置别名:用于指定要配置别名的包,当指定之后,该包下的实体类都会注册别名,并且类名就是别名,不再区分大小写-->
<package name="com.itheima.domain"></package>
</typeAliases>
这里的package和上面的有区别,这里是配置dao接口的,上面是配置实现类的。
<!--配置映射文件的位置-->
<mappers>
<!--<mapper resource="com/itheima/dao/IUserDao.xml"></mapper>-->
<!--package标签是用于指定dao接口所在的包,当指定之后就不需要再写mapper以及resource或者class-->
<package name="com.itheima.dao"></package>
</mappers>