文章目录
1.CRUD
在MyBatis框架下进行CRUD就十分简单了,只需要两步:
- 1.在UserMapper.xml映射文件中写sql语句
- 2.编写测试代码
映射文件
<mapper namespace="UserMapper">
<!-- resultType="com.kk.demo1.User"表明sql语句查询的结果封装到User实体类中-->
<select id="findAll" resultType="com.kk.demo1.User">
select * from users
</select>
<!-- 插入操作,将User对象的属性插入表中,parameterType="要插入的数据类型"
注意#{id}对应的是 User对象的第一个属性名,不是数据库的字段名
-->
<insert id="add" parameterType="com.kk.demo1.User">
insert into users values(#{id},#{username},#{password})
</insert>
<!-- 修改操作-->
<update id="update" parameterType="com.kk.demo1.User">
update users set username=#{username},password=#{password} where id=#{id}
</update>
<!-- 删除操作,这里直接写int可以成功是因为MyBatis为Integer设置好了别名int,相应的String——string,Boolean——boolean等-->
<delete id="del" parameterType="int">
delete from users where id=#{id}
</delete>
</mapper>
测试代码
@Test
public void test() throws IOException {
//获得核心配置文件,注意不要导错包import org.apache.ibatis.io.Resources
InputStream sqlMapConfig = Resources.getResourceAsStream("SqlMapConfig.xml");
//获得session工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(sqlMapConfig);
//获得session(会话)对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//执行sql语句,只有这里会变
// //1.查询
// List<User> list = sqlSession.selectList("UserMapper.findAll");
// System.out.println(list);
// //2.插入
// User user = new User();
// //user.setId(4);
// user.setUsername("kk");
// user.setPassword("123");
// sqlSession.insert("UserMapper.add",user);
// //MyBatis默认是不提交事务的,因此执行更新操作(增删改)时需要提交事务
// sqlSession.commit();
// //3.修改
// User user = new User();
// user.setId(3);
// user.setUsername("kk");
// user.setPassword("123");
// sqlSession.update("UserMapper.update",user);
// sqlSession.commit();
//4.删除,当多条件判断时仍需要传入User对象
sqlSession.delete("UserMapper.del",4);
sqlSession.commit();
//释放资源
sqlSession.close();
}
2.MyBatis类的分析
- SqlSessionFactoryBuilder.build(InputStream)
前面我们写过如下代码://通过Resources类 将MyBatis核心文件加载为输入流,以此构建SqlSessionFactory工厂对象 InputStream sqlMapConfig = Resources.getResourceAsStream("SqlMapConfig.xml"); //获得session工厂对象 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(sqlMapConfig); /* 1.Mybatis读取XML配置文件后会将内容放在一个Configuration类中 SqlSessionFactoryBuilder会读取Configuration类中信息创建SqlSessionFactory 2.初始化SqlSessionFactory时,Mapper 接口进行注册, 注册在了名为 MapperRegistry 类的 HashMap中,key = Mapper class, value = 当前创建的Mapper的工厂 */
- SqlSessionFactory,用来创建一个SqlSession对象(类似于JDBC中的Connection类),用来和数据库交互
- openSession() 默认开启一个事务(即创建sqlSession对象),但不会自动提交,要通过sqlSession.commit()手动提交
- openSession(boolean autoCommit) 参数设为true则会自动提交事务
- SqlSession对象,可以执行sql语句和操作事务
- < T> T selectOne(“sql语句位置”,参数(对象或基本数据类型)) 返回查询到的一个结果
- < T> T selectList(“sql语句位置”,参数) 返回查询的数组
- int insert(“sql语句位置”,参数)
- int update(“sql语句位置”,参数)
- int delete(“sql语句位置”,参数)
- void commit() 提交事务
- void rollback() 回滚
3.动态代理实现Dao层
应当注意名称的一致性,否则会出现找不到类等错误
动态代理
UserDao userDao = sqlSession.getMapper(UserDao.class);
//userdao.method
//获取对应的Mapper,让映射器通过命名空间和方法名称找到对应的SQL,发送给数据库执行
4.映射文件详解(动态sql和sql片段抽取)
UserDao接口中新添两个方法
public interface UserDao {
public List<User> findAll() throws IOException;
public List<User> findByCondition(User user) throws IOException;
public List<User> findByIds(List<Integer> ids) throws IOException;
}
在映射文件中编写动态sql并抽取相同的sql片段
<mapper namespace="com.kk.dao.UserDao">
<!-- sql语句抽取-->
<sql id="selectUser">select * from users</sql>
<select id="findByCondition" parameterType="user" resultType="user">
<include refid="selectUser"></include>
<where>
<if test="id!=0">
and id=#{id}
</if>
<if test="username!=null">
and username=#{username}
</if>
<if test="password!=null">
and password=#{password}
</if>
</where>
</select>
<!-- List->list 如果传入的是数组参数类型应写array-->
<select id="findByIds" parameterType="list" resultType="user">
-- 相当于select * from users where id in() 或 id=1 or id=2...
<include refid="selectUser"></include>
<where>
<foreach collection="list" open="id in(" close=")" item="id" separator=",">
#{id}
</foreach>
</where>
</select>
</mapper>
5.核心配置文件详解
5.1 标签简介
- properties 加载外部properties文件
- typeAliases 设置类型别名
- environments 配置数据源环境
- typeHandler 配置自定义类型处理器,将数据库的jdbc类型和java中的类型进行相互转换https://cloud.tencent.com/developer/article/1549181
- plugins 配置插件
5.2 抽取数据库信息为.properties文件
SqlMapConfig.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 resource="jdbc.properties"/>
<!-- 定义别名-->
<typeAliases>
<typeAlias type="com.kk.demo1.User" alias="user"/>
</typeAliases>
<!-- 数据源环境,默认情况下使用id="mysql"环境-->
<environments default="mysql">
<environment id="mysql">
<!--transactionManager事务管理器:JDBC——可以使用提交和回滚,依赖于connection来管理事务;MANAGED——让容器来管理事务的生命周期,默认会关闭连接-->
<transactionManager type="JDBC"/>
<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="com/kk/mapper/UserMapper.xml"/>
</mappers>
</configuration>
jdbc.properties
jdbc.driver = com.mysql.cj.jdbc.Driver
jdbc.url = jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
jdbc.username = root
jdbc.password = root
最后,标签的书写顺序是有规定的
6.多表操作
6.1一对一查询
新建两个数据库表(orders和users),一对一查询即一个订单对应一个用户
Order实体类
public class Order {
private int id;
private Date ordertime;
private double total;
//表示当前订单属于哪个用户
private User user;
//别忘了在核心配置文件中起别名 <typeAlias type="com.kk.demo1.Order" alias="order"/>,方便后续操作
OrderDao接口,这里仍使用动态代理实现
public interface OrderDao {
public List<Order> findAll() throws IOException;
}
编写sql语句以及如何将查询结果进行封装
<mapper namespace="com.kk.dao.OrderDao">
<!-- 这里type="order"(表示该映射将封装到order中)正确,是因为在核心配置文件中设置了别名-->
<resultMap id="orderMap" type="order">
<!-- 指定 字段(column) 和对象属性(property) 映射关系-->
<id column="oid" property="id"/>
<result column="ordertime" property="ordertime"/>
<result column="total" property="total"/>
<!-- 对象封装方式-->
<!-- 封装方式1-->
<!-- <result column="uid" property="user.id"/>-->
<!-- <result column="username" property="user.username"/>-->
<!-- <result column="password" property="user.password"/>-->
<!-- 封装方式2 property指order中的属性名称(user), javaType指order中的属性类型(User)-->
<association property="user" javaType="user">
<result column="uid" property="id"/>
<result column="username" property="username"/>
<result column="password" property="password"/>
</association>
</resultMap>
<select id="findAll" resultMap="orderMap">
select *,o.id oid from `orders` o,`users` u where o.uid=u.id
</select>
SqlYog中查询结果
测试结果,代码与之前类似,不再赘述
6.2一对多查询
一对多查询即一个用户可以有多个订单
在User实体类中添加存储Order的集合
public class User {
private int id;
private String username;
private String password;
private List<Order> orderList;
}
编写sql语句以及如何将查询结果进行封装
<resultMap id="userMap" type="user">
<id column="uid" property="id"/>
<result column="username" property="username"/>
<result column="password" property="password"/>
<!-- 集合的配置 property指集合名称,ofType指集合中的数据类型-->
<collection property="orderList" ofType="order">
<!-- 封装order属性-->
<id column="oid" property="id"/>
<result column="ordertime" property="ordertime"/>
<result column="total" property="total"/>
</collection>
</resultMap>
<select id="findOneToMore" resultMap="userMap">
select *,o.id oid from users u, orders o where u.id=o.uid
</select>
测试结果
6.3多对多查询
如下,创建三个表(这里例子举得不太恰当,用户不可能即是经理又是职员…)
新增实体类Role
public class Role {
private int id;
private String roleName;
}
在User实体类中添加存储Role的集合
public class User {
private int id;
private String username;
private String password;
private List<Order> orderList;
private List<Role> roleList;
}
编写sql语句以及如何将查询结果进行封装
<resultMap id="user_roleMap" type="user">
<id column="user_id" property="id"/>
<result column="username" property="username"/>
<result column="password" property="password"/>
<collection property="roleList" ofType="role">
<id column="role_id" property="id"/>
<result column="rolename" property="roleName"/>
</collection>
</resultMap>
<select id="findMoreToMore" resultMap="user_roleMap">
SELECT * FROM users u,`user_role` ur,`role` r WHERE u.id=ur.user_id AND ur.role_id=r.id
</select>
测试结果
7.注解的使用
使用注解就不需要再写映射文件了,可以简化很多操作
7.1 CRUD
定义接口,使用注解进行sql语句的编写
public interface UserMapper {
@Insert("insert into users values(#{id},#{username},#{password})")
public void insert(User user);
@Update("update users set username=#{username},password=#{password} where id=#{id}")
public void update(User user);
@Delete("delete from users where id=#{id}")
public void del(int id);
@Select("select * from users where id=#{id}")
public User findById(int id);
@Select("select * from users")
public List<User> findAll();
}
注意,虽然不用加载映射文件了,但仍需要在核心配置文件中添加映射关系
<!-- 使用注解不用加载映射文件,但仍需要加载映射关系(因为映射关系始终存在)-->
<mappers>
<!-- 指定接口UserMapper所在的包-->
<package name="com.kk.mapper"/>
</mappers>
测试代码,只列举一个
public class TestAnno {
private UserMapper userMapper;
@Before
public void before() throws IOException {
InputStream sqlMapConfig = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(sqlMapConfig);
SqlSession sqlSession = sqlSessionFactory.openSession(true);
userMapper = sqlSession.getMapper(UserMapper.class);
}
@Test
public void add(){
User user = new User();
user.setUsername("kk");
user.setPassword("123");
userMapper.insert(user);
}
}
7.2 一对一查询@One
一个订单对应一个用户
实现方式1
public interface OrderMapper {
@Select("select *,o.id oid from `orders` o,`users` u where o.uid=u.id")
@Results({
@Result(column = "oid", property = "id"),
@Result(column = "ordertime", property = "ordertime"),
@Result(column = "total", property = "total"),
@Result(column = "uid", property = "user.id"),
@Result(column = "username", property = "user.username"),
@Result(column = "password", property = "user.password")
})
public List<Order> findAll();
}
实现方式2
public interface OrderMapper {
@Select("select * from orders")
@Results({
@Result(column = "id", property = "id"),
@Result(column = "ordertime", property = "ordertime"),
@Result(column = "total", property = "total"),
@Result(
property = "user", //要封装的属性名
column = "uid", //根据此字段再次查询users表
javaType = User.class, //要封装的实体类型
//@One代替了assocation标签,指定查询返回的单一对象
//这里不能写sql语句,而是调用UserMapper接口中的方法
//格式:@Result(column="",property="",one=@One(select=""))
one = @One(select = "com.kk.mapper.UserMapper.findById")
)
})
public List<Order> findAll();
}
测试结果
7.3 一对多查询@Many
一个用户可能有多个订单
UserMapper接口
//一对多查询
@Select("select * from users")
@Results({
@Result(id = true,column = "id",property = "id"),
@Result(column = "username",property = "username"),
@Result(column = "password",property = "password"),
@Result(
property = "orderList",
column = "id",
javaType = List.class,
many = @Many(select = "com.kk.mapper.OrderMapper.findByUid")
)
})
public List<User> findOneToMore();
在OrderMapper接口中新添
@Select("select * from orders where uid=#{uid}")
public List<Order> findByUid(int uid);
测试结果
7.4 多对多查询
仍是这个案例
UserMapper接口
//多对多查询
@Select("select * from users")
@Results({
@Result(id = true,column = "id",property = "id"),
@Result(column = "username",property = "username"),
@Result(column = "password",property = "password"),
@Result(
property = "roleList",
column = "id", //以users表中的字段id再次进行查询
javaType = List.class,
many = @Many(select = "com.kk.mapper.RoleMapper.findByUserId")
)
})
public List<User> findMoreToMore();
新增RoleMapper接口
public interface RoleMapper {
@Select("select * from `user_role` ur,`role` r WHERE ur.user_id=#{uid} AND ur.role_id=r.id")
public List<Role> findByUserId(int uid);
}
测试结果