是一个持久化框架。是一个半自动的ORM框架。前面的版本叫ibatis,后面迁移到google code时更名为Mybatis,现在已经迁移到github上。
持久化:将一个转瞬即逝的对象保存下来。在软件行业中,持久化一般指将内存中的数据保存到文件(数据库文件)中。
ORM:object relative mapping对象关系映射。对象:Java实体类对象,关系:关系型数据库。映射:关联。
简单来说,就是指将数据库中的某一个表,与Java实体类相关联,表中字段与实体类的属性相关联。
半自动:针对全自动而言的,是指在操作数据库时,需要编写相应的SQL语句,指定相应的映射关系,结果会自动映射。
1、新建maven工程,导入依赖
<dependencies>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.4.6version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
<scope>testscope>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.40version>
dependency>
dependencies>
2、编写核心配置文件
DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC">transactionManager>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql://localhost:3306/myshop?useUnicode=true&characterEncoding=utf-8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
dataSource>
environment>
environments>
<mappers>
<mapper resource="ProductMapper.xml">mapper>
mappers>
configuration>
3、编写映射文件
DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="a">
<select id="b" resultType="java.lang.Long">
SELECT COUNT(1) FROM product
select>
mapper>
4、编写测试代码
package com.qf.day52.test;
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.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
public class ProductDAOTest {
private SqlSessionFactory factory;
// 在下面的测试方法调用之前调用
@Before
public void before(){
try {
// 读取核心配置文件
InputStream inputStream = Resources.getResourceAsStream("mybatisCfg.xml");
// 创建连接工厂(会话工厂)、工厂模式
factory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
@Test
public void testCount(){
// 获得连接
SqlSession session = factory.openSession();
// 数据库操作
Long count = session.selectOne("a.b");
System.out.println(count);
// 关闭连接
session.close();
}
}
注意:
1、Mybatis其实不需要实体类和DAO就可以实现数据库操作。
2、实体类的作用:a、封装传入的多个参数(Mybatis框架只允许传一个参数)。b、封装查询结果。
3、DAO的作用:
DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="ProductDAO">
<select id="count" resultType="java.lang.Long">
SELECT COUNT(1) FROM product
select>
<delete id="delete" parameterType="java.lang.Integer">
DELETE FROM product WHERE p_id = #{id}
delete>
mapper>
// 注意,增删改操作需要提交事务
@Test
public void testDelete(){
// 获得连接
SqlSession session = factory.openSession();
// 数据库操作
// count表示所影响的行数
int count = session.delete("ProductDAO.delete", 27);
System.out.println(count);
// 提交事务
session.commit();
// 关闭连接
session.close();
}
DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="ProductDAO">
<sql id="productColumns">
p_id, t_id, p_name, p_time, p_image, p_price, p_state, p_info
sql>
<select id="count" resultType="java.lang.Long">
SELECT COUNT(1) FROM product
select>
<delete id="delete" parameterType="java.lang.Integer">
DELETE FROM product WHERE p_id = #{id}
delete>
<insert id="save">
INSERT INTO product(t_id, p_name, p_time, p_image, p_price, p_state, p_info)
VALUES (#{tid}, #{pname}, #{ptime}, #{pimage}, #{pprice}, #{pstate}, #{pinfo})
insert>
<update id="update">
UPDATE product SET t_id = #{tid}, p_name = #{pname}, p_time = #{ptime},
p_image = #{pimage}, p_price = #{pprice}, p_state = #{pstate}, p_info = #{pinfo}
WHERE p_id = #{pid}
update>
<resultMap id="productMap" type="com.qf.day52.entity.Product">
<id property="pid" column="p_id">id>
<result property="tid" column="t_id">result>
<result property="pname" column="p_name">result>
<result property="ptime" column="p_time">result>
<result property="pimage" column="p_image">result>
<result property="pprice" column="p_price">result>
<result property="pstate" column="p_state">result>
<result property="pinfo" column="p_info">result>
resultMap>
<select id="findAll" resultMap="productMap">
SELECT
<include refid="productColumns">include>
FROM product
select>
<select id="findById" resultMap="productMap">
SELECT
<include refid="productColumns">include>
FROM product WHERE p_id = #{pid}
select>
mapper>
public class ProductDAOTest {
private SqlSessionFactory factory;
// 在下面的测试方法调用之前调用
@Before
public void before(){
try {
// 读取核心配置文件
InputStream inputStream = Resources.getResourceAsStream("mybatisCfg.xml");
// 创建连接工厂(会话工厂)、工厂模式
factory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
@Test
public void testCount(){
// 获得连接
SqlSession session = factory.openSession();
// 数据库操作
Long count = session.selectOne("ProductDAO.count");
System.out.println(count);
// 关闭连接
session.close();
}
// 注意,增删改操作需要提交事务
@Test
public void testDelete(){
// 获得连接
SqlSession session = factory.openSession();
// 数据库操作
// count表示所影响的行数
int count = session.delete("ProductDAO.delete", 27);
System.out.println(count);
// 提交事务
session.commit();
// 关闭连接
session.close();
}
@Test
public void testSave(){
// 获得连接
SqlSession session = factory.openSession();
// 数据库操作
// 由于Mybatis只能传一个参数,所以当有多个参数需要传递时,可以将其封装成实体类对象或者Map
/*********************************** 第一种方式:使用map*************************
Map map = new HashMap();
// #{tid}, #{pname}, #{ptime}, #{pimage}, #{pprice}, #{pstate}, #{pinfo}
// 此处map中应该使用tid等#{}中的名称作为key,要传进去的内容作为值
map.put("tid", 1);
map.put("pname", "华为P40");
map.put("ptime", new Date());
map.put("pimage", "aaaa");
map.put("pprice", 30);
map.put("pstate", 1);
map.put("pinfo", "好");
session.insert("ProductDAO.save", map);
******************************第一种方式end*******************************/
/**************************第二种方式:使用实体类(常用方式)*************/
// #{tid}, #{pname}, #{ptime}, #{pimage}, #{pprice}, #{pstate}, #{pinfo}
// 此处应该使用tid等#{}中的名称作为属性名称,要传进去的内容作为属性值
Product product = new Product();
product.setTid(2);
product.setPname("华为P40 pro");
product.setPimage("bbbb");
product.setPtime(new Date());
product.setPprice(40.0);
product.setPstate(1);
product.setPinfo("非常好");
session.insert("ProductDAO.save", product);
/***************************第二种方式end*********************************/
// 提交事务
session.commit();
// 关闭连接
session.close();
}
@Test
public void update(){
// 获得连接
SqlSession session = factory.openSession();
// 数据库操作
Product product = new Product();
product.setPid(28);
product.setTid(2);
product.setPname("华为P40 pro");
product.setPimage("bbbb");
product.setPtime(new Date());
product.setPprice(40.0);
product.setPstate(1);
product.setPinfo("非常好");
session.insert("ProductDAO.update", product);
// 提交事务
session.commit();
// 关闭连接
session.close();
}
@Test
public void testFindAll(){
// 获得连接
SqlSession session = factory.openSession();
// 数据库操作
List<Product> list = session.selectList("ProductDAO.findAll");
System.out.println(list);
// 关闭连接
session.close();
}
@Test
public void testFindById(){
// 获得连接
SqlSession session = factory.openSession();
// 数据库操作
Product product = session.selectOne("ProductDAO.findById", 1);
System.out.println(product);
// 关闭连接
session.close();
}
}
使用Mybatis框架,可以不需要使用DAO,只要使用映射文件即可。
但是我们发现,有一些问题:
1、不能根据方法名称了解该方法作用。
2、对于在映射文件中定义的方法名称,使用字符串的形式调用,无法有效编译提示正确性。
3、对于方法参数类型无法有效提示。
4、对于方法的返回值类型,也无法有效提示。
为了让代码编写过程中,代码可以有效的编译提示,我们用反射的方式,使用接口来映射mapper文件中的方法。
package com.qf.day52.dao;
import com.qf.day52.entity.Product;
import java.util.List;
// 此接口作用为映射mapper文件
// 方法名称对应操作的id
// 参数和返回值类型也需要一一对应
// 接口需要对应namespace,注意应该全名称对应
public interface ProductDAO {
Long count();
int delete(Integer id);
int save(Product product);
int update(Product product);
List<Product> findAll();
Product findById(Integer id);
}
package com.qf.day52.test;
import com.qf.day52.dao.ProductDAO;
import com.qf.day52.entity.Product;
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.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
public class ProductDAOTest1 {
private SqlSessionFactory factory;
// 在下面的测试方法调用之前调用
@Before
public void before(){
try {
// 读取核心配置文件
InputStream inputStream = Resources.getResourceAsStream("mybatisCfg.xml");
// 创建连接工厂(会话工厂)、工厂模式
factory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
@Test
public void testCount(){
// 获取连接
SqlSession session = factory.openSession();
// 通过连接获取接口的映射对象
ProductDAO productDAO = session.getMapper(ProductDAO.class);
// 数据库操作
// 使用断言
Assert.assertTrue(productDAO.count() > 0);
// 关闭连接
session.close();
}
// 注意,增删改操作需要提交事务
@Test
public void testDelete(){
// 获得连接
SqlSession session = factory.openSession();
// 数据库操作
// 通过连接获取接口的映射对象
ProductDAO productDAO = session.getMapper(ProductDAO.class);
Assert.assertTrue(productDAO.delete(29) == 1);
// 提交事务
session.commit();
// 关闭连接
session.close();
}
@Test
public void testSave(){
// 获得连接
SqlSession session = factory.openSession();
// 数据库操作
Product product = new Product();
product.setTid(2);
product.setPname("华为P40 pro");
product.setPimage("bbbb");
product.setPtime(new Date());
product.setPprice(40.0);
product.setPstate(1);
product.setPinfo("非常好");
ProductDAO productDAO = session.getMapper(ProductDAO.class);
Assert.assertTrue(productDAO.save(product)==1);
// 提交事务
session.commit();
// 关闭连接
session.close();
}
@Test
public void update(){
// 获得连接
SqlSession session = factory.openSession();
// 数据库操作
Product product = new Product();
product.setPid(28);
product.setTid(2);
product.setPname("华为P40 pro");
product.setPimage("bbbb");
product.setPtime(new Date());
product.setPprice(40.0);
product.setPstate(1);
product.setPinfo("非常好");
ProductDAO productDAO = session.getMapper(ProductDAO.class);
Assert.assertTrue(productDAO.update(product)==1);
// 提交事务
session.commit();
// 关闭连接
session.close();
}
@Test
public void testFindAll(){
// 获得连接
SqlSession session = factory.openSession();
// 数据库操作
ProductDAO productDAO = session.getMapper(ProductDAO.class);
Assert.assertTrue(productDAO.findAll().size() > 20);
// 关闭连接
session.close();
}
@Test
public void testFindById(){
// 获得连接
SqlSession session = factory.openSession();
// 数据库操作
ProductDAO productDAO = session.getMapper(ProductDAO.class);
Assert.assertNotNull(productDAO.findById(1));
// 关闭连接
session.close();
}
}
经典面试题:#和$的区别:
#
相当于占位符?,可以防止sql注入#
在传入参数时,需要对应对象的属性或者map的key,当只有一个参数时,可以随意$
相当于字符串拼接,有sql注入的风险$
在传入参数,只能对应对象的属性或map的key,即使只有一个参数,也需要传入对象或者map,可以用@Param注解自动封装map$
的使用场景:一般用在需要动态传入表名或列名等关键内容时。
[难点]
一对一:项目中使用并不多,也可以整合成一张表。例如:用户基本信息和用户详情。两张表操作的频率不一致,所以拆分以提升性能。如果新建两个实体类,在任何一个实体类中添加另一个实体类的属性皆可。意味着可以在任何一张表添加对应另一张表的外键。
一对多(多对一):部门和员工是一对多的关系。班级和学生是一对多的关系。多对一是一对多的反向描述。员工和部门就是多对一的关系。在表设计时,需要在多方添加外键。在实体类设计时,在多方(员工)里面需要设置一方(部门)的对象,在一方(订单)可以设置多方(订单项)的集合。
多对多:学生和课程是多对多的关系。角色和权限是多对多的关系。在表设计上,需要新建中间表来保存两者的多对多关系。在实体类设计上,在任何一方都可以写一个对应的另一方的集合。
此处使用产品和类型作为案例。
实体类封装为:
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Product {
private Integer pid;
private String pname;
private Date ptime;
private String pimage;
private Double pprice;
private Integer pstate;
private String pinfo;
private Type type;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Type {
private Integer tid;
private String tname;
private String tinfo;
}
映射文件编写:
ProductMapper.xml
DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.qf.day53.dao.ProductDAO">
<sql id="productColumns">
p_id, t_id, p_name, p_time, p_image, p_price, p_state, p_info
sql>
<resultMap id="productMap" type="com.qf.day53.entity.Product">
<id property="pid" column="p_id">id>
<result property="pname" column="p_name">result>
<result property="ptime" column="p_time">result>
<result property="pimage" column="p_image">result>
<result property="pprice" column="p_price">result>
<result property="pstate" column="p_state">result>
<result property="pinfo" column="p_info">result>
<association property="type" column="t_id" select="com.qf.day53.dao.TypeDAO.findById">association>
resultMap>
<select id="findAll" resultMap="productMap">
SELECT
<include refid="productColumns">include>
FROM product
select>
<select id="findById" resultMap="productMap">
SELECT
<include refid="productColumns">include>
FROM product
WHERE p_id = #{id}
select>
mapper>
TypeMapper.xml
DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.qf.day53.dao.TypeDAO">
<sql id="typeColumns">
t_id,t_name,t_info
sql>
<resultMap id="typeMap" type="com.qf.day53.entity.Type">
<id property="tid" column="t_id">id>
<result property="tname" column="t_name">result>
<result property="tinfo" column="t_info">result>
resultMap>
<select id="findById" resultMap="typeMap">
SELECT
<include refid="typeColumns">include>
FROM `type`
WHERE t_id = #{id}
select>
mapper>
public class ProductDAOTest {
private SqlSessionFactory factory;
@Before
public void setUp() throws Exception {
InputStream inputStream = Resources.getResourceAsStream("mybatisCfg.xml");
factory = new SqlSessionFactoryBuilder().build(inputStream);
}
@Test
public void findAll() throws Exception {
SqlSession session = factory.openSession();
ProductDAO productDAO = session.getMapper(ProductDAO.class);
System.out.println(productDAO.findAll());
session.close();
}
@Test
public void findById() throws Exception {
SqlSession session = factory.openSession();
ProductDAO productDAO = session.getMapper(ProductDAO.class);
System.out.println(productDAO.findById(1));
session.close();
}
}
以订单和订单项为案例。
与上面的多对一查询的关联类似,区别在于查询结果为一个集合。
OrderMapper.xml
DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.qf.day53.dao.OrderDAO">
<sql id="orderColumns">
o_id, u_id, a_id, o_count, o_time, o_state
sql>
<resultMap id="orderMap" type="com.qf.day53.entity.Orders">
<id property="oid" column="o_id">id>
<result property="uid" column="u_id">result>
<result property="aid" column="a_id">result>
<result property="ocount" column="o_count">result>
<result property="otime" column="o_time">result>
<result property="ostate" column="o_state">result>
<collection property="items" column="o_id" select="com.qf.day53.dao.ItemDAO.findByOrderId">collection>
resultMap>
<select id="findById" resultMap="orderMap">
SELECT
<include refid="orderColumns">include>
FROM `orders`
WHERE o_id = #{id}
select>
mapper>
ItemMapper.xml
DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.qf.day53.dao.ItemDAO">
<sql id="itemColumns">
i_id, o_id, p_id, i_count, i_num
sql>
<resultMap id="itemMap" type="com.qf.day53.entity.Item">
<id property="iid" column="i_id">id>
<result property="oid" column="o_id">result>
<result property="icount" column="i_count">result>
<result property="inum" column="i_num">result>
<association property="product" column="p_id" select="com.qf.day53.dao.ProductDAO.findById">association>
resultMap>
<select id="findByOrderId" resultMap="itemMap">
SELECT
<include refid="itemColumns">include>
FROM `item`
WHERE o_id = #{id}
select>
mapper>
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Orders {
private String oid;
private Integer uid;
private Integer aid;
private Double ocount;
private Date otime;
private Integer ostate;
private List<Item> items;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Item {
private Integer iid;
private String oid;
private Product product;
private Double icount;
private Integer inum;
}
问题1:在查询商品时关联查询商品类型,发现有29条商品数据,只有2条类型数据,所以类型表只被查询了2次,而不是29次。
问题2:在不需要显示类型时也会查询类型表。
问题3:如果设置了lazy,当关闭连接后,循环打印类型信息时,每一次打印都会自动开启连接并查询,查询完毕后关闭连接。一共查询了29次类型。
答案:
1、在连接开启到连接关闭过程中,查询的所有数据都会被暂时的放到内存中缓存起来,如果在关闭连接之前,还需要使用该数据,会先在缓存中查询是否存在该数据,如果存在,则直接使用,而不需要去数据库查询。
2、因为默认为eager(渴望的),可以设置为lazy(懒加载,需要的时候才查询)。
上面的订单和订单项案例中,如果查询订单详情,应该关联查询订单项,但是如果是查询所有的订单信息列表,不会显示订单项,此时不应该关联查询订单项。
可以编写两个resultMap,使用继承的方式来实现配置的复用。
<resultMap id="orderMap" type="com.qf.day53.entity.Orders">
<id property="oid" column="o_id">id>
<result property="uid" column="u_id">result>
<result property="aid" column="a_id">result>
<result property="ocount" column="o_count">result>
<result property="otime" column="o_time">result>
<result property="ostate" column="o_state">result>
resultMap>
<resultMap id="orderDetailMap" type="com.qf.day53.entity.Orders" extends="orderMap">
<collection property="items" fetchType="eager" column="o_id" select="com.qf.day53.dao.ItemDAO.findByOrderId">collection>
resultMap>
仅仅为了演示操作过程,使用一个不是很恰当的案例:
添加一个类型的同时,添加该类型的产品。
问题:
类型id是自增的,当添加完类型时,根本不知道id是多少,紧接着要添加的产品需要该类型id作为外键。
TypeMapper.xml
<insert id="save" useGeneratedKeys="true" keyProperty="tid">
INSERT INTO `type`(t_name,t_info) VALUES (#{tname}, #{tinfo})
insert>
@Test
public void save() throws Exception {
SqlSession session = factory.openSession();
TypeDAO typeDAO = session.getMapper(TypeDAO.class);
// 创建Type对象并添加
Type type = new Type();
type.setTname("笔记本");
type.setTinfo("好");
typeDAO.save(type);
ProductDAO productDAO = session.getMapper(ProductDAO.class);
// 创建产品对象并添加
Product product = new Product();
// 此处使用的type对象的tid属性,但是上面创建过程中并没有设置,是通过在配置文件中配置的type的save方法中使用自增并回填的方式获取的tid属性。
product.setType(type);
product.setPname("华为笔记本");
product.setPimage("bbbb");
product.setPtime(new Date());
product.setPprice(4000.0);
product.setPstate(1);
product.setPinfo("非常好");
productDAO.save(product);
session.commit();
session.close();
}
[重点]
根据条件动态生成的sql语句。而不是一开始就写好的静态语句。
判断属性是否存在,如果存在则拼接相应的sql。
注意:在动态sql的标签中pname名称,必须是一个对象的属性名称或者是map对应的key
<select id="findAll" resultMap="productMap">
SELECT
<include refid="productColumns">include>
FROM product
WHERE 1 = 1
<if test="pname != null">
AND p_name LIKE #{pname}
if>
select>
public interface ProductDAO {
// 可以使用@Param注解,作用是将当前的参数封装到一个map中,以注解中的名称作为map的key
List<Product> findAll(@Param("pname") String pname);
}
相当于条件分支,补充上面的if的不足。用法类似于jstl中的。
注意:条件分支只会执行一个。
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG WHERE state = ‘ACTIVE’
<choose>
<when test="title != null">
AND title like #{title}
when>
<when test="author != null and author.name != null">
AND author_name like #{author.name}
when>
<otherwise>
AND featured = 1
otherwise>
choose>
select>
where:能够通过判断条件是否有,如果没有自动去掉where,如果有一个条件,会去掉条件前面的AND或者OR,以where代替。
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG
<where>
<if test="state != null">
state = #{state}
if>
<if test="title != null">
AND title like #{title}
if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
if>
where>
select>
set:会自动去掉最后一个修改列后面的逗号
<update id="updateAuthorIfNecessary">
update Author
<set>
<if test="username != null">username=#{username},if>
<if test="password != null">password=#{password},if>
<if test="email != null">email=#{email},if>
<if test="bio != null">bio=#{bio}if>
set>
where id=#{id}
update>
[难点]
trim是where、set的原生写法,也就是说可以处理相应的字符串,比如去掉前面的and,去掉后面的
,号
,还可以在前后添加一些内容。where和set由于功能比较常用,所以单独写了标签。
<insert id="save">
INSERT INTO product
<trim suffixOverrides="," prefix="(" suffix=")VALUES (">
<if test="type != null and type.tid != null">
t_id,
if>
<if test="pname != null">
p_name,
if>
<if test="ptime != null">
p_time,
if>
trim>
<trim suffixOverrides="," suffix=")">
<if test="type != null and type.tid != null">
#{type.tid},
if>
<if test="pname != null">
#{pname},
if>
<if test="ptime != null">
#{ptime},
if>
trim>
insert>
foreach循环拼接sql。
例如需要查询id为3,5,8的数据。
一般情况下sql可能会select … where id in (3, 5, 8)
<select id="findAllByIds" resultMap="productMap">
SELECT
<include refid="productColumns">include>
FROM product
WHERE
p_id IN
<foreach collection="list" open="(" close=")" item="o" separator=",">
#{o}
foreach>
select>
public interface ProductDAO {
List<Product> findAllByIds(List<Integer> ids);
}
用来封装接口方法中多个参数,封装成map传入到sql操作中。参考上面6.1if使用。
User login(@Param("username") String username, @Param("password") String password);
<select id="login" resultType="com.qf.day54.entity.User">
select * from `user` where username = #{username} and password = #{password}
select>
代替mapper中对应的增删改查操作
public interface TypeDAO {
@Select("select count(1) from `type`")
Long count();
@Insert({"insert into `type`(t_name, t_info) values (#{tname}, #{tinfo})"})
int save(Type type);
@Results(id = "typeMap",
value = {
@Result(property = "tid", column = "t_id", id = true),
@Result(property = "tname", column = "t_name"),
@Result(property = "tinfo", column = "t_info"),
@Result(property = "productList", column = "t_id", many = @Many(select = "com.qf.day54.ProductDAO.findAllByTypeId", fetchType = FetchType.LAZY))
}
)
@Select("select * from `type`")
List<Type> findAll();
@ResultMap("typeMap")
@Select("select * from `type` where t_id = #{id}")
Type findById(Integer tid);
@UpdateProvider(type = TypeDAOProvider.class, method = "update")
int update(Type type);
}
public class TypeDAOProvider {
public String update(Type type){
SQL sql = new SQL();
sql.UPDATE("type");
if (type.getTname() != null){
sql.SET("t_name", "#{tname}");
}
if (type.getTinfo() != null){
sql.SET("t_info", "#{tinfo}");
}
sql.WHERE("t_id", "#{tid}");
return sql.toString();
}
}
注意:如果是纯注解,那么在核心配置文件中需要使用class来对应。
mybatisCfg.xml
<mappers>
<mapper resource="mapper/ProductMapper.xml">mapper>
<mapper class="com.qf.day54.dao.TypeDAO">mapper>
mappers>
注意:可以映射文件和注解同时存在,但是不能冲突。在注解中可以引用映射文件中的内容,比如@ResultMap注解。