文章目录
Mybatis
Mybatis 简介
Mybatis 入门
在这一部分,我们由于是新手需要快速的入门,这里采用了how2java这一网站提供的教程和相关的源代码资料。
从查询开始
首先,我们使用MySQL数据库,然后建立一个我们项目的数据库。
然后建立一个简单的表格,向其中添加数据。
接着开始Java程序的编写。
首先,写一个表对应的实体类Category。
package com.how2java.pojo;
public class Category {
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
下面是mybatis的核心配置。
- 别名(typeAliases):指示了类名查找的地方。
- 下面是数据库环境配置(environment):可以有多个环境,选择其中一个应用。需要配置如事务管理器、url、驱动、数据源、用户名和密码。
- 映射(mappers):一些相关的映射配置文件。
<?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>
<typeAliases>
<package name="com.how2java.pojo"/>
</typeAliases>
<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/how2java?characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="admin"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/how2java/pojo/Category.xml"/>
</mappers>
</configuration>
下一步,我们书写一个简单的Category的映射文件。
其中,namespace标识了命名空间,select是一条语句,id表示调用它使用的标识。resultType是返回值,由于我们前面已经配置了别名,所以使用Category即可。
<?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">
<mapper namespace="com.how2java.pojo">
<select id="listCategory" resultType="Category">
select * from category_
</select>
</mapper>
最后一步,就是测试一下我们的查询语句了。
根据mybatis-config.xml"
配置文件,我们能够创建一个SqlSessionFactory,然后根据SqlSessionFactory打开一个session,通过这个session来执行我们刚刚配置的查询语句。通过查询,可以获取到数据库表中的内容并将其输出。
package com.how2java;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
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 com.how2java.pojo.Category;
public class TestMybatis {
public static void main(String[] args) throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session=sqlSessionFactory.openSession();
List<Category> cs=session.selectList("listCategory");
for (Category c : cs) {
System.out.println(c.getName());
}
}
}
增删改查
一个增删改查的映射配置文件:已经写好了各种sql语句。
<?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">
<mapper namespace="com.how2java.pojo">
<insert id="addCategory" parameterType="Category" >
insert into category_ ( name ) values (#{name})
</insert>
<delete id="deleteCategory" parameterType="Category" >
delete from category_ where id= #{id}
</delete>
<select id="getCategory" parameterType="_int" resultType="Category">
select * from category_ where id= #{id}
</select>
<update id="updateCategory" parameterType="Category" >
update category_ set name=#{name} where id=#{id}
</update>
<select id="listCategory" resultType="Category">
select * from category_
</select>
</mapper>
先构造一个对象,然后选择添加语句和对象,查询所有的列表,然后提交事务、关闭会话。
下面是添加语句的测试方法:
Category c = new Category();
c.setName("新增加的Category");
session.insert("addCategory",c);
listAll(session);
session.commit();
session.close();
下面是删除语句的测试方法:
Category c = new Category();
c.setId(6);
session.delete("deleteCategory",c);
下面是查询语句的测试方法:
Category c= session.selectOne("getCategory",3);
System.out.println(c.getName());
下面是修改语句的测试方法:
Category c= session.selectOne("getCategory",3);
c.setName("修改了的Category名稱");
session.update("updateCategory",c);
模糊查询
添加一个简单的配置文件:
这是mysql语句的写法。
如果是oracle语句的写法select * from category_ where name like '%'||#{0}||'%'
。
<mapper namespace="com.how2java.pojo">
<select id="listCategoryByName" parameterType="string" resultType="Category">
select * from category_ where name like concat('%',#{0},'%')
</select>
</mapper>
测试语句比较简单:
List<Category> cs = session.selectList("listCategoryByName","cat");
多条件查询
在原来模糊查询的基础上,添加了一个id条件。
<select id="listCategoryByIdAndName" parameterType="map" resultType="Category">
select * from category_ where id> #{id} and name like concat('%',#{name},'%')
</select>
因为这是一个多参数,而函数中只需要存一个参数,通过传入参数可以查询到相应的结果。
Map<String,Object> params = new HashMap<>();
params.put("id", 3);
params.put("name", "cat");
List<Category> cs = session.selectList("listCategoryByIdAndName",params);
一对多关系
一个简单的Product实体类:
包含 id ,产品名,价格。
package com.how2java.pojo;
public class Product {
private int id;
private String name;
private float price;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}
@Override
public String toString() {
return "Product [id=" + id + ", name=" + name + ", price=" + price + "]";
}
}
一个简单的Category 实体类,包含id、目录名和一个产品列表。
package com.how2java.pojo;
import java.util.List;
public class Category {
private int id;
private String name;
List<Product> products;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Product> getProducts() {
return products;
}
public void setProducts(List<Product> products) {
this.products = products;
}
@Override
public String toString() {
return "Category [id=" + id + ", name=" + name + "]";
}
}
我们要访问Category来实现一对多的关系,无需使用Product。
下面是一个简单的Category映射文件:
这里用了ResultMap而不是ResultType来实现一对多的关系。
所以首先定义一个ResultMap。
在查询语句中,因为出现了同名字段,需要别名来进行区分。
<?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">
<mapper namespace="com.how2java.pojo">
<resultMap type="Category" id="categoryBean">
<id column="cid" property="id" />
<result column="cname" property="name" />
<!-- 一对多的关系 -->
<!-- property: 指的是集合属性的值, ofType:指的是集合中元素的类型 -->
<collection property="products" ofType="Product">
<id column="pid" property="id" />
<result column="pname" property="name" />
<result column="price" property="price" />
</collection>
</resultMap>
<!-- 关联查询分类和产品表 -->
<select id="listCategory" resultMap="categoryBean">
select c.*, p.*, c.id 'cid', p.id 'pid', c.name 'cname', p.name 'pname' from category_ c left join product_ p on c.id = p.cid
</select>
</mapper>
最后就是测试代码:
总的来说比较简单易懂。
List<Category> cs = session.selectList("listCategory");
for (Category c : cs) {
System.out.println(c);
List<Product> ps = c.getProducts();
for (Product p : ps) {
System.out.println("\t"+p);
}
}
多对一关系
下面根据上面的代码,继续实现多对一关系。
首先,为Product添加Category属性。
private Category category;
下面使用编写Product的映射文件:
使用association 进行多对一关系关联,指定表字段名称与对象属性名称的一一对应关系。使用的sql语句是一样的。
<?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">
<mapper namespace="com.how2java.pojo">
<resultMap type="Product" id="productBean">
<id column="pid" property="id" />
<result column="pname" property="name" />
<result column="price" property="price" />
<!-- 多对一的关系 -->
<!-- property: 指的是属性名称, javaType:指的是属性的类型 -->
<association property="category" javaType="Category">
<id column="cid" property="id"/>
<result column="cname" property="name"/>
</association>
</resultMap>
<!-- 根据id查询Product, 关联将Orders查询出来 -->
<select id="listProduct" resultMap="productBean">
select c.*, p.*, c.id 'cid', p.id 'pid', c.name 'cname', p.name 'pname' from category_ c left join product_ p on c.id = p.cid
</select>
</mapper>
最后在mybatis文件中添加Product的映射配置。然后测试:
List<Product> ps = session.selectList("listProduct");
for (Product p : ps) {
System.out.println(p+" 对应的分类是 \t "+ p.getCategory());
}
多对多关系
首先,我们进行模型分析:
一张订单里 可以包含多种产品,即Order。
一种产品 可以出现在多张订单里,即Product。
那么此时我们的产品和订单即为多对多的关系。
所以我们需要一个中间表,来指示订单和产品的对应关系。即OrderItem。
那么首先我们建立订单类:
包含订单号,和订单项目。
package com.how2java.pojo;
import java.util.List;
public class Order {
private int id;
private String code;
List<OrderItem> orderItems;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public List<OrderItem> getOrderItems() {
return orderItems;
}
public void setOrderItems(List<OrderItem> orderItems) {
this.orderItems = orderItems;
}
}
一个订单项目表格,ProductItem,其中包括数量,从属的订单和产品。
package com.how2java.pojo;
public class OrderItem {
private int id;
private int number;
private Order order;
private Product product;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
public Order getOrder() {
return order;
}
public void setOrder(Order order) {
this.order = order;
}
public Product getProduct() {
return product;
}
public void setProduct(Product product) {
this.product = product;
}
}
对应的一个简单的Order映射配置文件:
它写了一个全部查询和单个查询两种Sql语句。
<?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">
<mapper namespace="com.how2java.pojo">
<resultMap type="Order" id="orderBean">
<id column="oid" property="id" />
<result column="code" property="code" />
<collection property="orderItems" ofType="OrderItem">
<id column="oiid" property="id" />
<result column="number" property="number" />
<association property="product" javaType="Product">
<id column="pid" property="id"/>
<result column="pname" property="name"/>
<result column="price" property="price"/>
</association>
</collection>
</resultMap>
<select id="listOrder" resultMap="orderBean">
select o.*,p.*,oi.*, o.id 'oid', p.id 'pid', oi.id 'oiid', p.name 'pname'
from order_ o
left join order_item_ oi on o.id =oi.oid
left join product_ p on p.id = oi.pid
</select>
<select id="getOrder" resultMap="orderBean">
select o.*,p.*,oi.*, o.id 'oid', p.id 'pid', oi.id 'oiid', p.name 'pname'
from order_ o
left join order_item_ oi on o.id =oi.oid
left join product_ p on p.id = oi.pid
where o.id = #{id}
</select>
</mapper>
一个简单的OrderItem的配置文件:
<?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">
<mapper namespace="com.how2java.pojo">
<insert id="addOrderItem" parameterType="OrderItem">
insert into order_item_
values(null,#{order.id},#{product.id},#{number})
</insert>
<insert id="deleteOrderItem" parameterType="OrderItem">
delete from order_item_
where oid = #{order.id} and pid = #{product.id}
</insert>
</mapper>
产品Product的简单映射配置:
<?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">
<mapper namespace="com.how2java.pojo">
<resultMap type="Product" id="productBean">
<id column="pid" property="id" />
<result column="pname" property="name" />
<result column="price" property="price" />
<!-- 多对一的关系 -->
<!-- property: 指的是属性名称, javaType:指的是属性的类型 -->
<association property="category" javaType="Category">
<id column="cid" property="id"/>
<result column="cname" property="name"/>
</association>
</resultMap>
<select id="listProduct" resultMap="productBean">
select c.*, p.*, c.id 'cid', p.id 'pid', c.name 'cname', p.name 'pname'
from category_ c
left join product_ p on c.id = p.cid
</select>
<select id="getProduct" resultMap="productBean">
select c.*, p.*, c.id 'cid', p.id 'pid', c.name 'cname', p.name 'pname'
from category_ c
left join product_ p on c.id = p.cid
where p.id = #{id}
</select>
</mapper>
然后将新增的配置加入到mybatis的配置文件中去。
接下来就是测试工作了:
对Order进行查询。
private static void listOrder(SqlSession session) {
List<Order> os = session.selectList("listOrder");
for (Order o : os) {
System.out.println(o.getCode());
List<OrderItem> ois= o.getOrderItems();
for (OrderItem oi : ois) {
System.out.format("\t%s\t%f\t%d%n", oi.getProduct().getName(),oi.getProduct().getPrice(),oi.getNumber());
}
}
}
添加订单项目功能的测试:
private static void addOrderItem(SqlSession session) {
Order o1 = session.selectOne("getOrder", 1);
Product p6 = session.selectOne("getProduct", 6);
OrderItem oi = new OrderItem();
oi.setProduct(p6);
oi.setOrder(o1);
oi.setNumber(200);
session.insert("addOrderItem", oi);
}
删除订单项目的功能的测试:
private static void deleteOrderItem(SqlSession session) {
Order o1 = session.selectOne("getOrder",1);
Product p6 = session.selectOne("getProduct",6);
OrderItem oi = new OrderItem();
oi.setProduct(p6);
oi.setOrder(o1);
session.delete("deleteOrderItem", oi);
}
待补充的功能:
- 删除订单同时删除订单项目。
- 修改订单项目的功能。
Mybatis 动态SQL
if 标签
通过配置下面的语句,可以让mybatis给我们动态地创建SQL语句,减少代码地重写,方便以后地维护。当给入name参数,执行name参数的模糊查询。
<select id="listProduct" resultType="Product">
select * from product_
<if test="name!=null">
where name like concat('%',#{name},'%')
</if>
</select>
下面是我们的测试语句:
System.out.println("查询所有的");
List<Product> ps = session.selectList("listProduct");
for (Product p : ps) {
System.out.println(p);
}
System.out.println("模糊查询");
Map<String,Object> params = new HashMap<>();
params.put("name","a");
List<Product> ps2 = session.selectList("listProduct",params);
for (Product p : ps2) {
System.out.println(p);
}
where/set/trim标签:解决多条件矛盾
if 多条件的矛盾
如下面的语句:
那么如name存在,price存在,那么出现的语句即是多出了and。
<select id="listProduct" resultType="Product">
select * from product_
<if test="name!=null">
where name like concat('%',#{name},'%')
</if>
<if test="price!=0">
and price > #{price}
</if>
</select>
这个问题可以通过where
标签来解决。
<select id="listProduct" resultType="Product">
select * from product_
<where>
<if test="name!=null">
and name like concat('%',#{name},'%')
</if>
<if test="price!=null and price!=0">
and price > #{price}
</if>
</where>
</select>
类似的在update语句中set
标签:
<set>
<if test="name != null">name=#{name},</if>
<if test="price != null">price=#{price}</if>
</set>
同样地,可以使用trim
标签来实现同样的效果:
<trim prefix="WHERE" prefixOverrides="AND |OR ">
...
</trim>
<trim prefix="SET" suffixOverrides=",">
...
</trim>
mybatis中的if else
在mybatis中使用when other来作为else 使用
<select id="listProduct" resultType="Product">
SELECT * FROM product_
<where>
<choose>
<when test="name != null">
and name like concat('%',#{name},'%')
</when>
<when test="price !=null and price != 0">
and price > #{price}
</when>
<otherwise>
and id >1
</otherwise>
</choose>
</where>
</select>
foreach标签:循环条件
可以传入条件列表,动态构造我们的MySQL
SELECT * FROM product_
WHERE ID in
<foreach item="item" index="index" collection="list"
open="(" separator="," close=")">
#{item}
</foreach>
如查找(1,3,5)的product出来。
bind标签:字符串连接
使用一个简单的bind
标签来连接我们的字符串方便后续使用。
<select id="listProduct" resultType="Product">
<bind name="likename" value="'%' + name + '%'" />
select * from product_ where name like #{likename}
</select>
mybatis 注解
CRUD注解
首先我们编写一个mapper 映射接口,就是将原来的xml配置文件中的内容移入到我们的接口中来实现。
下面是使用的各种Sql语句。
package com.how2java.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import com.how2java.pojo.Category;
public interface CategoryMapper {
@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();
}
使用下面的mapper替换掉原来mybatis配置的mapper:
<mapper class="com.how2java.mapper.CategoryMapper"/>
下面就是一个简单的测试了:
首先通过我们的mybatis配置文件获取到资源,通过资源的InputStream拿到一个SqlSessionFactory,然后创建出一个session,通过session获取到映射,实现一个映射接口
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = sqlSessionFactory.openSession();
CategoryMapper mapper = session.getMapper(CategoryMapper.class);
下面是各种实现增删查改的函数:
private static void update(CategoryMapper mapper) {
Category c= mapper.get(8);
c.setName("修改了的Category名稱");
mapper.update(c);
listAll(mapper);
}
private static void get(CategoryMapper mapper) {
Category c= mapper.get(8);
System.out.println(c.getName());
}
private static void delete(CategoryMapper mapper) {
mapper.delete(2);
listAll(mapper);
}
private static void add(CategoryMapper mapper) {
Category c = new Category();
c.setName("新增加的Category");
mapper.add(c);
listAll(mapper);
}
private static void listAll(CategoryMapper mapper) {
List<Category> cs = mapper.list();
for (Category c : cs) {
System.out.println(c.getName());
}
}
一对多关系注解
对应的Category的接口类如下面这么写, 在Select注释中写我们的select语句,然后备注一个Result注释返回类型,获取Category 的id 和Category id对应的product对象集合。
package com.how2java.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Many;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import com.how2java.pojo.Category;
public interface CategoryMapper {
@Select(" select * from category_ ")
@Results({
@Result(property = "id", column = "id"),
@Result(property = "products", javaType = List.class, column = "id", many = @Many(select = "com.how2java.mapper.ProductMapper.listByCategory") )
})
public List<Category> list();
}
下面是一个简单的product注解:
这个product注解给定了一条语句,通过cid来获取product集合。
package com.how2java.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Select;
import com.how2java.pojo.Product;
public interface ProductMapper {
@Select(" select * from product_ where cid = #{cid}")
public List<Product> listByCategory(int cid);
}
然后将上面的接口如前面的方法配置到mybatis的配置文件之中即可。
最后,我们测试一下:
private static void listAll(CategoryMapper mapper) {
List<Category> cs = mapper.list();
for (Category c : cs) {
System.out.println(c.getName());
List<Product> ps = c.getProducts();
for (Product p : ps) {
System.out.println("\t"+p.getName());
}
}
}
多对一关系注解
在Category中提供通过id查询的sql接口:
package com.how2java.mapper;
import org.apache.ibatis.annotations.Select;
import com.how2java.pojo.Category;
public interface CategoryMapper {
@Select(" select * from category_ where id = #{id}")
public Category get(int id);
}
在Product中通过一对一的查询方法, 利用id查询,获取到相应的内容:
package com.how2java.mapper;
import java.util.List;
import org.apache.ibatis.annotations.One;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import com.how2java.pojo.Product;
public interface ProductMapper {
@Select(" select * from product_ ")
@Results({
@Result(property="category",column="cid",one=@One(select="com.how2java.mapper.CategoryMapper.get"))
})
public List<Product> list();
}
下面,同样地进行一下简单的测试:
ProductMapper mapper = session.getMapper(ProductMapper.class);
List<Product> ps= mapper.list();
for (Product p : ps) {
System.out.println(p + "\t对应的分类是:\t" + p.getCategory().getName());
}
多对多关系注解
按照上一节逻辑,只不过这一次使用注解的方式来实现相同的功能。
首先,给Product添加一个简单地通过id查询的sql接口。
package com.how2java.mapper;
import org.apache.ibatis.annotations.Select;
import com.how2java.pojo.Product;
public interface ProductMapper {
@Select("select * from product_ where id = #{id}")
public Product get(int id);
}
一个OrderItem类:
通过表现OrderItem和Product多对一的关系来实现。
package com.how2java.mapper;
import java.util.List;
import org.apache.ibatis.annotations.One;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import com.how2java.pojo.OrderItem;
public interface OrderItemMapper {
@Select(" select * from order_item_ where oid = #{oid}")
@Results({
@Result(property="product",column="pid",one=@One(select="com.how2java.mapper.ProductMapper.get"))
})
public List<OrderItem> listByOrder(int oid);
}
一个Order类:Order订单类中包含很多个OrderItem订单项目。
通过给定一个Order,能够找到多个OrderItem,OrderItem对应Product。
由此实现了Order和Product之间多对多的关系。
package com.how2java.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Many;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import com.how2java.pojo.Order;
public interface OrderMapper {
@Select("select * from order_")
@Results({
@Result(property = "id", column = "id"),
@Result(property = "orderItems", javaType = List.class, column = "id",
many = @Many(select = "com.how2java.mapper.OrderItemMapper.listByOrder"))
})
public List<Order> list();
}
最后,简单修改一个mybatis配置文件中的mapper配置,然后进行一番测试即可:
private static void listOrder(SqlSession session) {
OrderMapper mapper =session.getMapper(OrderMapper.class);
List<Order> os = mapper.list();
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());
}
}
}
}
注解实现动态SQL
例如以前sql语句,现在我们利用注解可以连sql语句都不用写了。
@Insert(" insert into category_ ( name ) values (#{name}) ")
public int add(Category category);
改动后:
@InsertProvider(type=CategoryDynaSqlProvider.class,method="add")
public int add(Category category);
于是,原来的CRUD可以改为:
public interface CategoryMapper {
@InsertProvider(type=CategoryDynaSqlProvider.class,method="add")
public int add(Category category);
@DeleteProvider(type=CategoryDynaSqlProvider.class,method="delete")
public void delete(int id);
@SelectProvider(type=CategoryDynaSqlProvider.class,method="get")
public Category get(int id);
@UpdateProvider(type=CategoryDynaSqlProvider.class,method="update")
public int update(Category category);
@SelectProvider(type=CategoryDynaSqlProvider.class,method="list")
public List<Category> list();
}
测试的话,使用之前增删改查的测试方法即可。
SQL类使用
可以通过SQL类来动态生成SQL语句。
private String selectPersonSql() {
return new SQL() {{
SELECT("P.ID, P.USERNAME, P.PASSWORD, P.FULL_NAME");
SELECT("P.LAST_NAME, P.CREATED_ON, P.UPDATED_ON");
FROM("PERSON P");
FROM("ACCOUNT A");
INNER_JOIN("DEPARTMENT D on D.ID = P.DEPARTMENT_ID");
INNER_JOIN("COMPANY C on D.COMPANY_ID = C.ID");
WHERE("P.ID = A.ID");
WHERE("P.FIRST_NAME like ?");
OR();
WHERE("P.LAST_NAME like ?");
GROUP_BY("P.ID");
HAVING("P.LAST_NAME like ?");
OR();
HAVING("P.FIRST_NAME like ?");
ORDER_BY("P.ID");
ORDER_BY("P.FULL_NAME");
}}.toString();
}
进阶
mybatis日志
有时候,我们想测试mybatis究竟干了些什么事情。可惜,mybatis自身没有日志的功能。
不过,我们可以采用第三方日志log4j jar包来实现日志输出。
下面前几行均为日志输出:
告诉了我们使用的sql语句,参数,相对的列和总共的执行数。
事务管理
这里我们采用的事务管理器是JDBC。当然还有其他的管理器。
<transactionManager type="JDBC"/>
下面我们执行一个简单的测试:
在下面的代码中,我们首先添加了一行合理的插入,而第二行是不合理的因为它的名字过长。由于二者处于同一个事务之中,所以第二行插入的错误导致了第一行也没有被插入。
package com.how2java;
import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;
import java.util.List;
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.apache.ibatis.transaction.Transaction;
import org.apache.ibatis.transaction.TransactionFactory;
import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
import com.how2java.mapper.CategoryMapper;
import com.how2java.pojo.Category;
public class TestMybatis {
public static void main(String[] args) throws IOException, SQLException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = sqlSessionFactory.openSession();
CategoryMapper mapper = session.getMapper(CategoryMapper.class);
Category c1 = new Category();
c1.setName("长度够短的名称");
mapper.add(c1);
Category c2 = new Category();
c2.setName("超过最大长度30的名称超过最大长度30的名称超过最大长度30的名称超过最大长度30的名称超过最大长度30的名称超过最大长度30的名称");
mapper.add(c2);
listAll(mapper);
session.commit();
session.close();
}
private static void listAll(CategoryMapper mapper) {
List<Category> cs = mapper.list();
for (Category c : cs) {
System.out.println(c.getName());
}
}
}
MySQL支持事务管理的一个条件就是innodb引擎。
可以使用下面的语句来修改我们的引擎。
alter table category_ ENGINE = innodb;
延迟加载
那延迟加载什么意思呢?就是在真的需要的时候,才去查询相关的内容。
比如我们前面使用了Category分类对应了许多的Product,当我们查询Category的时候,如果不需要使用到Product属性,那么就不对Product表进行查询,等到我们需要用到了,再执行Product表的查询,就是将查询语句的执行延迟了。
一个简单的延迟加载的配置:
<?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>
<settings>
<!-- 打开延迟加载的开关 -->
<setting name="lazyLoadingEnabled" value="true" />
<!-- 将积极加载改为消息加载即按需加载 -->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
<typeAliases>
<package name="com.how2java.pojo"/>
</typeAliases>
<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/how2java?characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="admin"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/how2java/pojo/Category.xml"/>
<mapper class="com.how2java.mapper.CategoryMapper"/>
<mapper class="com.how2java.mapper.ProductMapper"/>
</mappers>
</configuration>
然后我们可以通过之前查询中,注释掉使用Product语句的方法,然后开启日志工具,两者比较一下,就可以了解到延迟加载已经生效了。
分页
分页是一个非常实用的功能,我们经常能用得到。
在xml配置方法中:
我们给它添加个limit,起始位置,和分页计数即可。
<select id="listCategory" resultType="Category">
select * from category_
<if test="start!=null and count!=null">
limit #{start},#{count}
</if>
</select>
下面是xml测试方法:
private static void xmlWay(SqlSession session) {
Map<String,Object> params = new HashMap<>();
params.put("start", 0);
params.put("count", 5);
List<Category> cs =session.selectList("listCategory", params);
for (Category c : cs) {
System.out.println(c);
}
}
注解方式:
下面是注解方式实现的mapper接口:
package com.how2java.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Many;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import com.how2java.pojo.Category;
public interface CategoryMapper {
@Select(" select * from category_ ")
@Results({@Result(property = "products", javaType = List.class, column = "id",
many = @Many(select = "com.how2java.mapper.ProductMapper.listByCategory"))})
public List<Category> list();
@Select(" select * from category_ limit #{start},#{count}")
public List<Category> listByPage(@Param("start") int start, @Param("count")int count);
}
下面是注解方式的测试方法:
private static void annotationWay(SqlSession session) {
CategoryMapper mapper = session.getMapper(CategoryMapper.class);
List<Category> cs =mapper.listByPage(0, 5);
for (Category c : cs) {
System.out.println(c);
}
}
分页进阶:PageHelper插件
首先它是一个第三方的mybatis插件,所以一定要配置一下jar包。
接着在我们的mybatis配置文件的主体中配置插件。
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
</plugin>
</plugins>
测试方法:
可以简单的通过PageHelper设置插件,然后通过PageInfo获取总数。
PageHelper.offsetPage(0, 5);
List<Category> cs = session.selectList("listCategory");
for (Category c : cs) {
System.out.println(c.getName());
}
PageInfo pageInfo = new PageInfo<>(cs);
System.out.println("总数:"+pageInfo.getTotal());
System.out.println(pageInfo);
一级缓存
在mybatis中,是带有缓存系统的。其中一级缓存处于每个session之中,就是说在一段时间我们通过同一个session获取相同的数据的时候,会被保存在session对应的一级缓存之中,如果通过不同的session来执行,那么需要重新查表。
二级缓存
于是,我们考虑在一级缓存的基础上实现一个二级缓存。
通过在Mybatis配置文件的setting
中配置开启缓存:
<setting name="cacheEnabled" value="true"/>
接着在mapper处添加cache
标签,表示开启二级缓存
<mapper namespace="com.how2java.pojo">
<cache/>
<insert id="addCategory" parameterType="Category" >
insert into category_ ( name ) values (#{name})
</insert>
<delete id="deleteCategory" parameterType="Category" >
delete from category_ where id= #{id}
</delete>
<select id="getCategory" parameterType="_int" resultType="Category">
select * from category_ where id= #{id}
</select>
<update id="updateCategory" parameterType="Category" >
update category_ set name=#{name} where id=#{id}
</update>
<select id="listCategory" resultType="Category">
select * from category_
<if test="start!=null and count!=null">
limit #{start},#{count}
</if>
</select>
</mapper>
下一步,修改我们Category 这样的pojo类,让他实现一个接口:
public class Category implements Serializable{
@Override
public String toString() {
return "Category [id=" + id + ", name=" + name + "]";
}
}
通过测试,我们可以了解到在同一个sessionFactory之下调用两次相关Category的查询,并没有真正重复查表,而是调用了缓存。
c3p0连接池的使用
在mybatis中想使用c3p0连接池,我们需要自己写一个类,继承于UnpooledDataSourceFactory
,并且手动指定数据源ComboPooledDataSource
。
package org.mybatis.c3p0;
import org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory;
import com.mchange.v2.c3p0.ComboPooledDataSource;
public class C3P0DataSourceFactory extends UnpooledDataSourceFactory{
public C3P0DataSourceFactory(){
this.dataSource =new ComboPooledDataSource();
}
}
修改mybatis的配置:
将数据源配置type="POOLED"
更换为type="org.mybatis.c3p0.C3P0DataSourceFactory"
的数据源。
逆向工程
Mybatis Generator是一个用于Mybatis逆向工程的工具。
以前,我们的程序设计,首先pojo、mapper、xml,然后创建表。
现在我们的程序设计采用逆向工程,首先建表,通过Mybatis Generator直接生成我们需要的pojo、mapper、xml相关的文件。
配置一个generator的一个配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<!--数据库驱动-->
<!--
如果IDE(eclipse或者idea) 项目里导入了jar包,那么就不需要配置了jar包的绝对路径了
<classPathEntry location="e:/project/mybatis/lib/mysql-connector-java-5.0.8-bin.jar"/>
-->
<context id="DB2Tables" targetRuntime="MyBatis3">
<commentGenerator>
<property name="suppressDate" value="true"/>
<property name="suppressAllComments" value="false"/>
</commentGenerator>
<!--数据库链接地址账号密码-->
<jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://localhost/how2java?useUnicode=true&characterEncoding=UTF-8" userId="root" password="123456">
</jdbcConnection>
<javaTypeResolver>
<property name="forceBigDecimals" value="false"/>
</javaTypeResolver>
<!--生成Model类存放位置-->
<javaModelGenerator targetPackage="com.how2java.pojo" targetProject="src">
<property name="enableSubPackages" value="true"/>
<property name="trimStrings" value="true"/>
</javaModelGenerator>
<!--生成映射文件存放位置-->
<sqlMapGenerator targetPackage="com.how2java.pojo" targetProject="src">
<property name="enableSubPackages" value="true"/>
</sqlMapGenerator>
<!--生成Dao类存放位置-->
<javaClientGenerator type="XMLMAPPER" targetPackage="com.how2java.mapper" targetProject="src">
<property name="enableSubPackages" value="true"/>
</javaClientGenerator>
<!--生成对应表及类名-->
<table tableName="category_" domainObjectName="Category" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="true" selectByExampleQueryId="false">
<property name="my.isgen.usekeys" value="true"/>
<generatedKey column="id" sqlStatement="JDBC"/>
</table>
<!-- <table tableName="product_" domainObjectName="Product" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"></table> -->
</context>
</generatorConfiguration>
generator的使用方法:
public static void main(String[] args) throws Exception {
List<String> warnings = new ArrayList<String>();
boolean overwrite = true;
InputStream is= TestMybatisGenerator.class.getClassLoader().getResource("generatorConfig.xml").openStream();
ConfigurationParser cp = new ConfigurationParser(warnings);
Configuration config = cp.parseConfiguration(is);
is.close();
DefaultShellCallback callback = new DefaultShellCallback(overwrite);
MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
myBatisGenerator.generate(null);
System.out.println("生成代码成功,刷新项目,查看文件,然后执行TestMybatis.java");
}
运行后,可以看到:
创建了一下的包和java/xml文件:
最后使用的测试方法:
CategoryExample example = new CategoryExample();
example.createCriteria().andNameLike("%9%");
CategoryMapper mapper = session.getMapper(CategoryMapper.class);
List<Category> cs= mapper.selectByExample(example);
for (Category c : cs) {
System.out.println(c.getName());
}