Java Mybatis 学习

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&amp;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());
        }

参考

  • 18
    点赞
  • 111
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值