1 什么叫注解
1.1 注解的作用
注解是添加在类、方法之前的以@开头的关键字。通过反射可以获得注解的内容。
1.2 注解的分类
- 元注解、基本内置注解
- 自定义注解
- 第三方注解
1.3 我们的文件结构
- 文件组成
- 模型文件包com.huang.model
- mapper映射文件包com.huang.mapper
- Demo测试文件包com.huang.test
- 配置文件
映射文件规范
在对应的映射文件里创建对应的方法。
- 在方法之前加上@insert/delete/select/update,注解的主体内容是SQL语句。
- 方法的参数表示可以往SQL语句中传入的参数
- 执行方式获取映射文件对应的映射器,执行方法。
- 映射文件
package com.huang.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Many;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import com.huang.model.Category;
public interface CategoryMapper {
//注解方式的sql映射
//接口中定义的成员变量,默认是用public static final 修饰的
//接口中定义的方法,默认是用public abstract 修饰的
//接口中定义的内部类,默认是用public static修饰的
//注解中的字符串是主要的查询语句
//参数表示往查询语句里传入的参数
@Insert(" insert into category_ ( name ) values (#{name}) ")
public int add(Category category);
@Delete(" delete from category_ where id= #{id} ")
public void delete(int id);
@Select("select * from category_ where id= #{id} ")
public Category get(int id);
@Update("update category_ set name=#{name} where id=#{id} ")
public int update(Category category);
@Select(" select * from category_ ")
public List<Category> list();
//查询出id、name、包含的商品
@Select(" select * from category_")
@Results({
@Result(property = "id", column = "id"),
@Result(property = "name", column = "name"),
//many属性表示一对多的关系,是另一个映射器里的查询结果
//把Category的id传入到查询根据cid查询Product的方法里,获得一个列表
@Result(property = "products", javaType = List.class, column = "id",
many = @Many(select = "com.huang.mapper.ProductMapper.list") )
})
public List<Category> listWithProduct();
}
- Demo文件
c. 配置文件中声明映射器//通过类对象获得映射器
CategoryMapper mapper=session.getMapper(CategoryMapper.class);
Category c=new Category();
c.setId(2);
c.setName("新的2号Category");
mapper.update(c);
<mappers>
<!-- 此处都用注解的方式 -->
<mapper class="com.huang.mapper.CategoryMapper" />
<mapper class="com.huang.mapper.ProductMapper" />
<mapper class="com.huang.mapper.OrderMapper" />
<mapper class="com.huang.mapper.OrderItemMapper" />
</mappers>
</configuration>
2.1 insert
- 映射文件
@Insert(" insert into category_ ( name ) values (#{name}) ")
public int add(Category category);
- Demo文件
System.out.println("增加:");
Category c=new Category();
c.setName("新的Category");
mapper.add(c);
2.2 delete
- 映射文件
@Delete(" delete from category_ where id= #{id} ")
public void delete(int id);
- Demo文件
System.out.println("删除1号:");
mapper.delete(1);
2.3 select
- 映射文件
查询单个与查询所有
@Select("select * from category_ where id= #{id} ")
public Category get(int id);
@Select(" select * from category_ ")
public List<Category> list();
- Demo文件
System.out.println("查询2号的名字:");
Category c= mapper.get(2);
System.out.println(c);
System.out.println("----------");
List<Category> cs=mapper.list();
for(Category c:cs) {
System.out.println(c);
}
System.out.println("----------");
2.4 update
- 映射文件
@Update("update category_ set name=#{name} where id=#{id} ")
public int update(Category category);
- Demo文件
System.out.println("更新2号的名字:");
Category c=new Category();
c.setId(2);
c.setName("新的2号Category");
mapper.update(c);
3 注解方式的映射
3.1 一对多与多对一
3.1.1 E-R图模型
E-R图模型:一个商品Product对应一个种类Category,一个种类Category对应多个商品Product。
3.1.2 分析
- 从Categoy角度来看:想要表示1对多的关系,需要获得所有Product.cid(类别号)为当前Category.id的Product对象。查询多条记录。
一个Category对应多个Product,因此显示所有时要用Many注解。
- 从Product角度来看:想要表示多对1的关系,只需要查询当前cid对应Category即可。查询一条记录。
一个Product对应一个Category,因此显示所有时要用One注解。
因此:在一对多的关系中,数据库内部是用外键实现的。但在MyBatis中需要通过查询来获得。
映射文件
- CategoryMapper,这是1这一方。
@Select("select * from category_ where id= #{id} ")
public Category get(int id);
@Select(" select * from category_")
@Results({
@Result(property = "id", column = "id"),
@Result(property = "name", column = "name"),
//many属性表示一对多的关系,是另一个映射器里的查询结果
//把Category的id传入到查询根据cid查询Product的方法里,获得一个列表
@Result(property = "products", javaType = List.class, column = "id",
many = @Many(select = "com.huang.mapper.ProductMapper.list") )
})
public List<Category> listWithProduct();
- ProductMapper,这是M这一方。
@Select("select * from product_ where cid=#{cid}")
public List<Product> list(int cid);
@Select("select * from product_")
@Results({ @Result(property = "id", column = "id"), @Result(property = "name", column = "name"),
@Result(property = "category", column = "cid", one = @One(select = "com.huang.mapper.CategoryMapper.get")) })
public List<Product> listWithCategory();
- Demo文件
- 测试从1这一方
CategoryMapper mapper = session.getMapper(CategoryMapper.class);
// 通过映射器进行查询
List<Category> cs = mapper.listWithProduct();
for (Category c : cs) {
//打印类别信息
System.out.println(c);
List<Product> ps = c.getProducts();
//打印商品信息
for (Product p : ps) {
System.out.println("\t"+p);
}
}
- 测试从M这一方
ProductMapper mapper = session.getMapper(ProductMapper.class);
// 通过映射器进行查询
List<Product> ps=mapper.listWithCategory();
for (Product p : ps) {
System.out.println(p+" 对应的分类是 \t "+ p.getCategory());
}
3.2 多对多
3.2.1 E-R图模型
E-R图模型:一个商品Product对应多个订单Order,一个订单Order对应多个商品Product。通过OrderItem这个中间表来联系。
本质还是两个一对多
3.2.2 分析
- 从Product角度来讲:想要表示需要获得所有OrderItem.pid(商品号)为当前Product.id的OrderItem对象。因此OrderItemMapper中应该有根据pid查多个OrderItem的方法。
一个Product对应多个OrderItem,显示所有时要用使用了Many注解。
- 从Order角度来讲:想要表示需要获得所有OrderItem.oid(订单号)为当前Order.id的OrderItem对象。因此OrderItemMapper中应该有根据oid查多个OrderItem的方法。
一个Product对应多个OrderItem,显示所有时要用使用了Many注解。
- 从OrderItem角度来看:想要表示多对1的关系,只需要查询当前pid对应Product即可、查询当前oid对应Order即可。查询一条记录。
因此OrderItemMapper中应该有根据pid查询一个Product的方法,使用了One注解,在OrderMapper中应该有根据oid查询一个Order的方法。
- 因此多对多的关系拆成两个一对多的关系。
3.2.3 代码
- 映射文件
- ProductMapper
// 根据id查询单个Product
@Select("select * from product_ where id = #{id}")
public Product get(int id);
@Select("select * from product_")
@Results({ @Result(property = "id", column = "id"),
@Result(property = "orderItems", javaType = List.class, column = "id", many = @Many(select = "com.huang.mapper.OrderItemMapper.listByProduct")) })
public List<Product> listWithOrderItem();
b.OrderMapper
// 根据id查询单个Order
@Select("select * from order_ where id = #{id}")
public Order get(int id);
@Select("select * from order_")
@Results({
@Result(property = "id", column = "id"),
@Result(property = "orderItems", javaType = List.class, column = "id",
many = @Many(select = "com.huang.mapper.OrderItemMapper.listByOrder"))
})
public List<Order> listWithOrderItem();
c.OrderItemMapper
//显示指定Order的id对应的所有订单条目,包含了Product对象
@Select(" select * from order_item_ where oid = #{oid}")
@Results({
@Result(property="product",column="pid",one=@One(select="com.huang.mapper.ProductMapper.get"))
})
public List<OrderItem> listByOrder(int oid);
//显示指定Product的id对应的所有订单条目,包含了Order对象
@Select(" select * from order_item_ where pid = #{pid}")
@Results({
@Result(property="order",column="oid",one=@One(select="com.huang.mapper.OrderMapper.get"))
})
public List<OrderItem> listByProduct(int pid);
- Demo文件
- 测试Product
// 通过类对象获得映射器
OrderMapper mapper = session.getMapper(OrderMapper.class);
List<Order> os = mapper.listWithOrderItem();
for (Order o : os) {
System.out.println(o.getCode());
List<OrderItem> ois = o.getOrderItems();
if (null != ois) {
for (OrderItem oi : ois) {
System.out.format("\t%s\t%f\t%d%n", oi.getProduct().getName(), oi.getProduct().getPrice(),
oi.getNumber());
}
}
}
- 测试Order
ProductMapper mapper1 = session.getMapper(ProductMapper.class);
List<Product> ps = mapper1.listWithOrderItem();
for (Product p : ps) {
System.out.println(p);
List<OrderItem> ois = p.getOrderItems();
for (OrderItem oi : ois) {
System.out.format("\t%s\t%d%n", oi.getOrder().getCode(), oi.getNumber());
}
}
4 动态SQL语句
4.1 动态SQL语句的文件及实现思路
- 新有一个sqlProvider类,其中的方法都返回了sql语句,通过MyBatis提供的SQL语句构建器实现了快速生成sql语句。https://mybatis.org/mybatis-3/zh/statement-builders.html
- mapper文件中通过注解应用了这个类,并且制定了sql语句。
4.2 代码
- CategorySqlProvider
package com.huang.sqlProvider;
import org.apache.ibatis.jdbc.SQL;
public class CategorySqlProvider {
//语句构建类
public String list() {
return new SQL()
.SELECT("*")
.FROM("category_")
.toString();
}
public String get() {
return new SQL()
.SELECT("*")
.FROM("category_")
.WHERE("id=#{id}")
.toString();
}
public String add(){
return new SQL()
.INSERT_INTO("category_")
.VALUES("name", "#{name}")
.toString();
}
public String update(){
return new SQL()
.UPDATE("category_")
.SET("name=#{name}")
.WHERE("id=#{id}")
.toString();
}
public String delete(){
return new SQL()
.DELETE_FROM("category_")
.WHERE("id=#{id}")
.toString();
}
}
- CategoryMapper
@InsertProvider(type=CategorySqlProvider.class,method="add")
public int add(Category category);
@DeleteProvider(type=CategorySqlProvider.class,method="delete")
public void delete(int id);
@SelectProvider(type=CategorySqlProvider.class,method="get")
public Category get(int id);
@UpdateProvider(type=CategorySqlProvider.class,method="update")
public int update(Category category);
@SelectProvider(type=CategorySqlProvider.class,method="list")
public List<Category> list();
- Demo文件与CRUD相同。
5 总结
5.1 基本规则
- 配置文件中声明方式
<mapper class="com.huang.mapper.CategoryMapper" />
- 映射文件的命名方式模型名+mapper。
- 获取对应映射文件的映射器。
//通过类对象获得映射器
CategoryMapper mapper=session.getMapper(CategoryMapper.class);
5.2 CRUD
@insert/delete/select/update注解。
5.3 一对多
1这一方用Many注解,多这一方用One注解。
- Many注解添加方式,以{}添加子注解
- Result注解中Property属性表示模型的属性,column表示sql查询语句中属性字段
- javaType为List.class表示集合类型的变量。
- Many注解指定的外部的column作为参数传入Many子注解的查询。
@Select("select * from product_")
@Results({ @Result(property = "id", column = "id"),
@Result(property = "orderItems", javaType = List.class, column = "id", many = @Many(select = "com.huang.mapper.OrderItemMapper.listByProduct")) })
- One注解添加方式
- One驻俄界指定的column作为参数传入One子注解的查询
@Select("select * from product_")
@Results({ @Result(property = "id", column = "id"), @Result(property = "name", column = "name"),
@Result(property = "category", column = "cid", one = @One(select = "com.huang.mapper.CategoryMapper.get")) })
5.4 多对多
5.5 动态SQL语句
- 先创建一个类通过SQL构建器提供一系列方法向外提供SQL语句。
- 映射文件中使用insert/delete/select/updateProvider,type属性指定上面这个类,method属性指明具体方法