Mybatis
一、MaBatis核心配置文件
Mybatis 中文文档
1. properties
定义属性及读取属性文件,取的时候用 $(name) ,name 为之前定义的name
定义属性
SqlMappingConfig.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <!--定义属性及读取属性文件--> <properties> <property name="jdbc.driver" value="com.mysql.jdbc.Driver" /> <property name="jdbc.url" value="jdbc:mysql://localhost:3307/mybatis?characterEncoding=utf-8" /> <property name="jdbc.username" value="root" /> <property name="jdbc.password" value="123456" /> </properties> <!--配置sql打印--> <settings> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings> <!-- spring整合后 environments配置将废除 使用spring中的连接池 --> <environments default="development"> <environment id="development"> <!-- 使用jdbc事务管理 --> <transactionManager type="JDBC"/> <!-- 数据库连接池 --> <dataSource type="POOLED"> <property name="driver" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </dataSource> </environment> </environments> <!--加载映射文件--> <mappers> <mapper resource="com/mybatis/domain/Customer.xml"></mapper> </mappers> </configuration>
读取属性文件
db.properties
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3307/mybatis?characterEncoding=utf-8 jdbc.username=root jdbc.password=123456
SqlMappingConfig.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <!--定义属性及读取属性文件--> <properties resource="db.properties"/> <!--配置sql打印--> <settings> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings> <!-- spring整合后 environments配置将废除 使用spring中的连接池 --> <environments default="development"> <environment id="development"> <!-- 使用jdbc事务管理 --> <transactionManager type="JDBC"/> <!-- 数据库连接池 --> <dataSource type="POOLED"> <property name="driver" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </dataSource> </environment> </environments> <!--加载映射文件--> <mappers> <mapper resource="com/mybatis/domain/Customer.xml"></mapper> </mappers> </configuration>
读取属性优先级
<!--定义属性及读取属性文件--> <properties resource="db.properties"> <!--如果外部配置文件有该属性,则内部定义的同名属性被外部属性覆盖--> <property name="jdbc.username" value="user" /> <property name="jdbc.password" value="1234" /> </properties>
2. settings
这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行。
settings 中的设置名可以查看文档。
这里举个驼峰命名法例子。
不开启驼峰命名法时,数据库里用下划线,domain里也要用下划线。
CREATE TABLE `customer` ( `cust_id` int(11) NOT NULL AUTO_INCREMENT, `cust_name` varchar(255) DEFAULT NULL, `cust_profession` varchar(255) DEFAULT NULL, `cust_phone` varchar(255) DEFAULT NULL, `email` varchar(255) DEFAULT NULL, PRIMARY KEY (`cust_id`) ) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8;
@Setter@Getter public class Customer { private Integer cust_id; private String cust_name; private String cust_profession; private String cust_phone; private String email; @Override public String toString() { return "Customer{" + "cust_id=" + cust_id + ", cust_name='" + cust_name + '\'' + ", cust_profession='" + cust_profession + '\'' + ", cust_phone='" + cust_phone + '\'' + ", email='" + email + '\'' + '}'; } }
如果数据库里用下划线,domain里用驼峰命名法。不开启驼峰命名法的情况下是取不到值的。
@Setter@Getter public class Customer { private Integer custId; private String custName; private String custProfession; private String custPhone; private String email; @Override public String toString() { return "Customer{" + "custId=" + custId + ", custName='" + custName + '\'' + ", custProfession='" + custProfession + '\'' + ", custPhone='" + custPhone + '\'' + ", email='" + email + '\'' + '}'; } }
@Test public void test2(){ SqlSession sqlSession = MybatisUtils.openSession(); CustomerMapper mapper = sqlSession.getMapper(CustomerMapper.class); Customer customerbyId = mapper.getCustomerById(2); System.out.println(customerbyId); }
上面代码会输出 Customer{custId=null, custName='null', custProfession='null', custPhone='null', email='libai@163.com'}
开启驼峰命名法之后就能取到值
<settings> <!--配置sql打印--> <setting name="logImpl" value="STDOUT_LOGGING"/> <!--开启驼峰命名法--> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings>
会输出 Customer{custId=2, custName='李白', custProfession='刺客', custPhone='18977665521', email='libai@163.com'}
3. typeAliases
类型别名是为 Java 类型设置一个短的名字。
定义单个别名
<!--定义别名--> <typeAliases> <!--单个别名定义--> <typeAlias alias="Customer" type="com.mybatis.domain.Customer"/> </typeAliases>
定义别名后,domain.xml中的,resultType 可以由全限定名称改为一个短的别名。
<!--根据cust_id查询客户--> <!--<select id="getCustomerById" parameterType="Integer" resultType="com.mybatis.domain.Customer">--> <select id="getCustomerById" parameterType="Integer" resultType="Customer"> SELECT * FROM `customer` WHERE cust_id = #{cust_id} </select>
批量别名定义
<!--定义别名--> <typeAliases> <!--批量定义别名, 别名为类名--> <package name="com.mybatis.domain"/> </typeAliases>
批量定义包下类的别名,将他们的别名设为类名。定义别名后,domain.xml中的,resultType 可以由全限定名称改为一个短的别名。
注意
如果当前包类与子包类重名,别名相同,会有异常。
这时候可以在类上使用注解 @Alias("别名"),起不同的别名。
4. typeHandlers
无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时, 都会用类型处理器将获取的值以合适的方式转换成 Java 类型。
JDK1.8之后实现全部的JSR310规范,日期时间处理上,我们可以使用MyBatis基于JSR310(Date and Time API)
编写的各种日期时间类型处理器。
MyBatis3.4以前的版本需要我们手动注册这些处理器,以后的版本都是自动注册的。
5. Plugins
插件是MyBatis提供的一个非常强大的机制,MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用。
通过插件来修改MyBatis的一些核心行为。
6. Environments
MyBatis可以配置多种环境,比如开发、测试和生产环境需要有不同的配置。
每种环境使用一个environment标签进行配置并指定唯一标识符,可以通过environments标签中的default属性指定一个环境的标识符来快速的切换环境
Environment子标签
(1)transactionManager 事务管理
type有以下取值:
① JDBC :使用JDBC 的提交和回滚设置,依赖于从数据源得到的连接来管理事务范围
② MANAGED :不提交或回滚一个连接、让容器来管理事务的整个生命周期;ManagedTransactionFactory
③ 自定义 :实现TransactionFactory接口 ;type=全类名/别名
(2)dataSource 数据源
type有以下取值:
① UNPOOLED :不使用连接池UnpooledDataSourceFactory
② POOLED :使用连接池PooledDataSourceFactory
③ JNDI :在EJB 或应用服务器这类容器中查找指定的数据源
④ 自定义 : 实现DataSourceFactory接口,定义数据源的获取方式
实际开发
实际开发中我们使用Spring管理数据源,并进行事务控制的配置来覆盖上述配置
7. databaseIDProvider
MyBatis 可以根据不同的数据库厂商执行不同的语句。
可以能过databaseIDProvider标签来进行设置。
<!--定义数据库厂商--> <databaseIdProvider type="DB_VENDOR"> <property name="MySQL" value="mysql"/> <property name="DB2" value="db2"/> <property name="Oracle" value="oracle" /> <property name="SQL Server" value="sqlserver"/> </databaseIdProvider>
databaseId 选择数据库厂商
<!--根据cust_id查询客户--> <select id="getCustomerById" parameterType="Integer" resultType="Customer" databaseId="mysql"> SELECT * FROM `customer` WHERE cust_id = #{cust_id} </select>
8. mappers
(1)<mapper resource=" ">
使用相对于类路径的资源。
<!--加载映射文件--> <mappers> <mapper resource="com/mybatis/domain/Customer.xml"></mapper> </mappers>
(2)<mapper class=" " />
使用mapper接口类路径。此种方法要求:
① mapper接口名称和mapper映射文件名称相同
② mapper接口和mapper映射文件放在同一个目录中(同包下)
<!--加载映射文件--> <mappers> <mapper class="com.mybatis.mapper.CustomerMapper"/> </mappers>
(3)指定包下的所有mapper接口。此种方法要求:
① mapper接口名称和mapper映射文件名称相同
② mapper接口和mapper映射文件放在同一个目录中(同包下)
<!--加载映射文件--> <mappers> <package name="com.mybatis.mapper"/> </mappers>
二、输出类型
1. 输出简单类型
<select id="getCount" resultType="Integer"> select count(*) from customer; </select>
2. Map
(1)第一种形式
key:是列名,value:是列名对应的值
CustomerMapper
public interface CustomerMapper { Integer getCount(); Map<String,Object> getbyId(Integer id); }
CustomerMapper.xml
<select id="getbyId" resultType="java.util.Map"> select * from customer where cust_id=#{id} </select>
Test
@Test public void test(){ SqlSession sqlSession = MybatisUtils.openSession(); CustomerMapper mapper = sqlSession.getMapper(CustomerMapper.class); Map<String, Object> map = mapper.getbyId(1); System.out.println(map); sqlSession.close(); }
output(key:是列名,value:是列名对应的值)
{cust_profession=射手, cust_name=鲁班, cust_id=1, cust_phone=13499887733, email=12341241@qq.com}
(2)第二种形式
Map<key, 自定义对象> :key为自己指定的列
CustomerMapper
public interface CustomerMapper { Integer getCount(); Map<String,Object> getbyId(Integer id); @MapKey("cust_id") Map<Integer,Customer> getAll(); }
CustomerMapper.xml
<select id="getAll" resultType="java.util.Map"> select * from customer; </select>
Test
@Test public void test() { SqlSession sqlSession = MybatisUtils.openSession(); CustomerMapper mapper = sqlSession.getMapper(CustomerMapper.class); Map<Integer, Customer> map = mapper.getAll(); for (Integer integer : map.keySet()) { System.out.println("Key:" + integer + "; Value:" + map.get(integer)); } sqlSession.close(); }
output(key为自己指定的列:@MapKey("cust_id"))
Key:1; Value:{cust_profession=射手, cust_name=鲁班, cust_id=1, cust_phone=13499887733, email=12341241@qq.com} Key:2; Value:{cust_profession=刺客, cust_name=李白, cust_id=2, cust_phone=18977665521, email=libai@163.com} Key:3; Value:{cust_profession=刺客, cust_name=阿轲, cust_id=3, cust_phone=18977665997, email=aike@qq.com} Key:4; Value:{cust_profession=肉盾, cust_name=德玛西亚, cust_id=4, cust_phone=13700997665, email=demaxiya.126.com6} Key:5; Value:{cust_profession=战士, cust_name=李信, cust_id=5, cust_phone=13586878987, email=yasuo@qq.com} Key:6; Value:{cust_profession=辅助, cust_name=奶妈, cust_id=6, cust_phone=13398909089, email=nama@qq.com} …………
3. resultMap
之有在写输出时使用的都是resultType,但是resultType要求必须得要字段名称和数据库当中的名称一致时才能有值,否则为null。
Customer
@Setter@Getter@ToString public class Customer { private Integer cust_id; private String cust_name; private String cust_profession; private String cust_phone; private String email; }
CustomerMapper
public interface CustomerMapper { Integer getCount(); Customer getbyId(Integer id); List<Customer> getAll(); }
CustomerMapper.xml
<select id="getbyId" resultType="Customer"> select * from customer where cust_id=#{id} </select> <select id="getAll" resultType="Customer"> select * from customer; </select>
Test
@Test public void testOne() { SqlSession sqlSession = MybatisUtils.openSession(); CustomerMapper mapper = sqlSession.getMapper(CustomerMapper.class); Customer customer = mapper.getbyId(2); System.out.println(customer); sqlSession.close(); } @Test public void testAll() { SqlSession sqlSession = MybatisUtils.openSession(); CustomerMapper mapper = sqlSession.getMapper(CustomerMapper.class); List<Customer> list = mapper.getAll(); for (Customer customer : list) { System.out.println(customer); } sqlSession.close(); }
如果sql查询字段名和pojo的属性名不一致,可以通过resultMap将字段名和属性名作一个对应关系。
Customer
@Setter@Getter@ToString public class Customer { private Integer id; private String name; private String profession; private String phone; private String email; }
CustomerMapper
public interface CustomerMapper { Integer getCount(); Customer getbyId(Integer id); List<Customer> getAll(); }
CustomerMapper.xml
<resultMap id="customerMap" type="Customer"> <!--id 定义的是主键--> <id column="cust_id" property="id"/> <result column="cust_name" property="name"/> <result column="cust_profession" property="profession"/> <result column="cust_phone" property="phone"/> <result column="email" property="email"/> </resultMap> <select id="getbyId" resultMap="customerMap"> select * from customer where cust_id=#{id} </select> <select id="getAll" resultMap="customerMap"> select * from customer; </select>
Test
@Test public void testOne() { SqlSession sqlSession = MybatisUtils.openSession(); CustomerMapper mapper = sqlSession.getMapper(CustomerMapper.class); Customer customer = mapper.getbyId(2); System.out.println(customer); sqlSession.close(); } @Test public void testAll() { SqlSession sqlSession = MybatisUtils.openSession(); CustomerMapper mapper = sqlSession.getMapper(CustomerMapper.class); List<Customer> list = mapper.getAll(); for (Customer customer : list) { System.out.println(customer); } sqlSession.close(); }
三、多表操作
1. 表之间关系
(1)一对一
一夫一妻
(2)一对多
一个部门有多个员工,一个员工只能属于某一个部门。
一个班级有多个学生,一个学生只能属于某一个班级。
一个客户有多个订单,一个订单只能属于某一个客户。
(3)多对多
一个老师教多个学生,一个学生可以被多个老师教。
一个学生可以先择多门课程,一门课程可以被多个学生选择。
一个用户可以选择多个角色,一个角色也可以被多个用户选择。
2. 表之间关系建表原则
(1)一对多
在多的一方创建一个外键,指向的一方的主键
(2)多对多
创建一个中间表,中间表至少有两个字段,分别作为外键指向多对多双方的主键
3. ManyToOne 多对一
一个客户有多个订单,一个订单只能属于某一个客户。
order 订单表
-- ---------------------------- -- Table structure for order -- ---------------------------- DROP TABLE IF EXISTS `order`; CREATE TABLE `order` ( `order_id` int(11) NOT NULL AUTO_INCREMENT, `order_name` varchar(255) DEFAULT NULL, `order_num` varchar(20) DEFAULT NULL, `cust_id` int(11) DEFAULT NULL, PRIMARY KEY (`order_id`), KEY `fk_order` (`cust_id`), CONSTRAINT `fk_order` FOREIGN KEY (`cust_id`) REFERENCES `customer` (`cust_id`) ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of order -- ---------------------------- INSERT INTO `order` VALUES ('1', '订单名称1', '10001', '2'); INSERT INTO `order` VALUES ('2', '订单名称2', '10002', '3'); INSERT INTO `order` VALUES ('3', '订单名称3', '10003', '2'); INSERT INTO `order` VALUES ('4', '订单名称4', '10004', '4');
(1)查询
Ⅰ)左连接查询
查询所有的订单及订单所对应的客户。
左连接:把左边表的数据全部查出,右边表只查出满足条件的记录。
sql:
SELECT * FROM `order` AS o LEFT JOIN customer AS c ON o.cust_id = c.cust_id
① 建立domain
Customer
@Setter@Getter@ToString public class Customer { private Integer cust_id; private String cust_name; private String cust_profession; private String cust_phone; private String email; }
Order
@Setter@Getter@ToString public class Order { private Integer order_id; private String order_name; private String order_num; private Customer customer; }
② 建立Mapping映射
OrderMapper
public interface OrderMapper { /*查询所有的订单*/ public List<Order> getAllOrders(); }
OrderMapper.xml
(a)不使用 association,同名的列(property和column相同)可以省略
<?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.mybatis.mapper.OrderMapper"> <resultMap id="orderMape" type="Order"> <id property="order_id" column="order_id"/> <result property="order_name" column="order_name"/> <result property="order_num" column="order_name"/> <!--级联属性赋值--> <result property="customer.cust_id" column="cust_id"/> <result property="customer.email" column="email"/> <result property="customer.cust_phone" column="cust_phone"/> <result property="customer.cust_profession" column="cust_profession"/> <result property="customer.cust_name" column="cust_name"/> </resultMap> <select id="getAllOrders" resultMap="orderMape"> SELECT * FROM `order` AS o LEFT JOIN customer AS c ON o.cust_id = c.cust_id </select> </mapper>
省略后:
<?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.mybatis.mapper.OrderMapper"> <resultMap id="orderMape" type="Order"> <!--级联属性赋值--> <result property="customer.cust_id" column="cust_id"/> <result property="customer.email" column="email"/> <result property="customer.cust_phone" column="cust_phone"/> <result property="customer.cust_profession" column="cust_profession"/> <result property="customer.cust_name" column="cust_name"/> </resultMap> <select id="getAllOrders" resultMap="orderMape"> SELECT * FROM `order` AS o LEFT JOIN customer AS c ON o.cust_id = c.cust_id </select> </mapper>
(b)使用 association(推荐,可以用分布查询),都不能省略
<?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.mybatis.mapper.OrderMapper"> <resultMap id="orderMape" type="Order"> <id property="order_id" column="order_id"/> <result property="order_name" column="order_name"/> <result property="order_num" column="order_name"/> <!--关联对象赋值--> <association property="customer" javaType="Customer"> <id property="cust_id" column="cust_id"/> <result property="cust_name" column="cust_name"/> <result property="cust_profession" column="cust_profession"/> <result property="cust_phone" column="cust_phone"/> <result property="email" column="email"/> </association> </resultMap> <select id="getAllOrders" resultMap="orderMape"> SELECT * FROM `order` AS o LEFT JOIN customer AS c ON o.cust_id = c.cust_id </select> </mapper>
③ 测试类
Test
@Test public void test(){ SqlSession sqlSession = MybatisUtils.openSession(); OrderMapper mapper = sqlSession.getMapper(OrderMapper.class); List<Order> list = mapper.getAllOrders(); for (Order order : list) { System.out.println(order); } sqlSession.close(); }
Ⅱ)分步查询
① 第一步 先查出所有的订单
OrderMapper.xml
<?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.mybatis.mapper.OrderMapper"> <resultMap id="orderMape" type="Order"> <id property="order_id" column="order_id"/> <result property="order_name" column="order_name"/> <result property="order_num" column="order_name"/> <!--关联对象赋值--> <association property="customer" javaType="Customer"> <id property="cust_id" column="cust_id"/> <result property="cust_name" column="cust_name"/> <result property="cust_profession" column="cust_profession"/> <result property="cust_phone" column="cust_phone"/> <result property="email" column="email"/> </association> </resultMap> <select id="getAllOrders" resultMap="orderMape"> SELECT * FROM `order` AS o LEFT JOIN customer AS c ON o.cust_id = c.cust_id </select> <resultMap id="resultMap" type="Order"> <id property="order_id" column="order_id"/> <result property="order_name" column="order_name"/> <result property="order_num" column="order_name"/> <!--分步查询--> <association property="customer" javaType="Customer" select="com.mybatis.mapper.CustomerMapper.getbyId" column="cust_id"> </association> </resultMap> <select id="getOrderbyId" resultMap="resultMap"> SELECT * FROM `order` WHERE order_id = #{id}; </select> </mapper>
② 第二步 根据id查出对应客户
CustomerMapper.xml
<?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.mybatis.mapper.CustomerMapper"> <select id="getCount" resultType="Integer"> select count(*) from customer; </select> <select id="getbyId" resultType="Customer"> select * from customer where cust_id=#{id} </select> <select id="getAll" resultType="Customer"> select * from customer; </select> </mapper>
③ 测试类
@Test public void test(){ SqlSession sqlSession = MybatisUtils.openSession(); OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class); Order order = orderMapper.getOrderbyId(1); System.out.println(order); sqlSession.close(); }
Ⅲ )分部查询懒加载
<settings> <!--配置sql打印--> <setting name="logImpl" value="STDOUT_LOGGING"/> <!--延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。--> <setting name="lazyLoadingEnabled" value="true"/> <!--当开启时,任何方法的调用都会加载该对象的所有属性。否则,每个属性会按需加载--> <setting name="aggressiveLazyLoading" value="false"/> <!--指定哪个对象的方法触发一次延迟加载。--> <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/> </settings>
(2)添加
先添加客户,获取客户生成的id,再去添加订单
① 添加客户
public interface CustomerMapper { Integer getCount(); Customer getbyId(Integer id); List<Customer> getAll(); /* 保存客户 */ public void insertCustomer(Customer customer); }
CustomerMapper.xml
<!--保存客户--> <!--useGeneratedKeys 使用生成的key--> <insert id="insertCustomer" parameterType="Customer" useGeneratedKeys="true" keyColumn="cust_id" keyProperty="cust_id"> insert into `customer`(cust_name,cust_profession,cust_phone,email) values (#{cust_name},#{cust_profession},#{cust_phone},#{email}) </insert>
② 添加订单
public interface OrderMapper { /*查询所有的订单*/ public List<Order> getAllOrders(); /*根据id查询订单*/ public Order getOrderbyId(Integer id); /*保存订单*/ public void insertOrder(Order order); }
OrderMapper.xml
<!--保存订单--> <insert id="insertOrder" parameterType="Order"> insert into `order`(order_name,order_num,cust_id) values (#{order_name},#{order_num},#{customer.cust_id}) </insert>
③ 测试
@Test public void test(){ SqlSession sqlSession = MybatisUtils.openSession(); CustomerMapper customerMapper = sqlSession.getMapper(CustomerMapper.class); OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class); Order order = new Order(); order.setOrder_name("新订单001"); order.setOrder_num("20000001001"); Customer customer = new Customer(); customer.setCust_name("新客户001"); customer.setCust_phone("137090909090"); customer.setEmail("123123@163.com"); customer.setCust_profession("新职业001"); /* 设置关系 */ order.setCustomer(customer); /* 先添加客户 获取客户生成的id 再去添加订单*/ customerMapper.insertCustomer(customer); System.out.println(customer); /* 保存订单 */ orderMapper.insertOrder(order); sqlSession.commit(); sqlSession.close(); }
4. OnToMany 一对多
(1)查询
Ⅰ)左连接查询
Customer
@Setter@Getter@ToString public class Customer { private Integer cust_id; private String cust_name; private String cust_profession; private String cust_phone; private String email; private List<Order> orders = new ArrayList<>(); }
CustomerMapper
public interface CustomerMapper { Integer getCount(); Customer getbyId(Integer id); /*保存客户*/ void insertCustomer(Customer customer); /*查询所有客户*/ List<Customer> getAllCustomers(); }
CustomerMapper.xml
<!--查询所有客户--> <select id="getAllCustomers" resultMap="custMap"> SELECT * FROM `customer` AS c LEFT JOIN `order` AS o ON c.cust_id = o.cust_id; </select> <resultMap id="custMap" type="Customer"> <id column="cust_id" property="cust_id"/> <result column="cust_name" property="cust_name"/> <result column="cust_profession" property="cust_profession"/> <result column="cust_phone" property="cust_phone"/> <result column="email" property="email"/> <collection property="orders" ofType="Order"> <id column="order_id" property="order_id"/> <id column="order_name" property="order_name"/> <id column="order_num" property="order_num"/> </collection> </resultMap>
测试
@Test public void test4(){ /*查询所有客户*/ SqlSession sqlSession = MybatisUtils.openSession(); CustomerMapper customerMapper = sqlSession.getMapper(CustomerMapper.class); List<Customer> allCustomers = customerMapper.getAllCustomers(); for (Customer allCustomer : allCustomers) { System.out.println(allCustomer); } sqlSession.close(); }
Ⅱ)分步查询
CustomerMapper.xml
<!--分步查询--> <select id="getAllCustomers" resultMap="custMap"> SELECT * FROM `customer`; </select> <resultMap id="custMap" type="Customer"> <id column="cust_id" property="cust_id"/> <result column="cust_name" property="cust_name"/> <result column="cust_profession" property="cust_profession"/> <result column="cust_phone" property="cust_phone"/> <result column="email" property="email"/> <collection property="orders" javaType="list" ofType="Order" select="com.mybatis.mapper.OrderMapper.getOrderbyCustId" column="cust_id"> </collection> </resultMap>
OrderMapper
public interface OrderMapper { /*查询所有的订单*/ List<Order> getAllOrders(); /*根据id查询订单*/ Order getOrderbyId(Integer id); /*保存订单*/ void insertOrder(Order order); /*根据cust_id查询订单*/ Order getOrderbyCustId(Integer id); }
OrderMapper.xml
<select id="getOrderbyCustId" resultType="Order"> SELECT * FROM `order` WHERE cust_id = #{id} </select>
测试和 左连接查询一样。
(2)添加
CustomerMapper.xml
<!--保存客户--> <!--useGeneratedKeys 使用生成的key--> <insert id="insertCustomer" parameterType="Customer" useGeneratedKeys="true" keyColumn="cust_id" keyProperty="cust_id"> insert into `customer`(cust_name,cust_profession,cust_phone,email) values (#{cust_name},#{cust_profession},#{cust_phone},#{email}) </insert>
OrderMapper
public interface OrderMapper { /*查询所有的订单*/ List<Order> getAllOrders(); /*根据id查询订单*/ Order getOrderbyId(Integer id); /*保存订单*/ void insertOrder(Order order); /*根据cust_id查询订单*/ Order getOrderbyCustId(Integer id); /*更新与客户的关系*/ void updateCustId(@Param("orderId") Integer orderId, @Param("custId") Integer custId); }
CustomerMapper.xml
<!--保存订单--> <insert id="insertOrder" parameterType="Order" useGeneratedKeys="true" keyColumn="order_id" keyProperty="order_id"> INSERT INTO `order` (order_name, order_num, cust_id) VALUES (#{order_name}, #{order_num}, #{customer.cust_id}) </insert> <update id="updateCustId"> UPDATE `order` SET `cust_id` = #{custId} WHERE `order_id` = #{orderId} </update>
测试
@Test public void test5(){ SqlSession sqlSession = MybatisUtils.openSession(); CustomerMapper customerMapper = sqlSession.getMapper(CustomerMapper.class); OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class); Customer customer = new Customer(); customer.setCust_name("新客户"); Order order1 = new Order(); order1.setOrder_name("订单2"); Order order2 = new Order(); order2.setOrder_name("订单2"); customer.getOrders().add(order1); customer.getOrders().add(order2); /*保存数据*/ customerMapper.insertCustomer(customer); orderMapper.insertOrder(order1); orderMapper.insertOrder(order2); /*更新关系*/ for (Order order : customer.getOrders()) { orderMapper.updateCustId(order.getOrder_id(),customer.getCust_id()); } sqlSession.commit(); sqlSession.close(); }
(3)删除
删除时一定要先打破关系再做删除操作
CustomerMapper
public interface CustomerMapper { Integer getCount(); Customer getbyId(Integer id); /*保存客户*/ void insertCustomer(Customer customer); /*查询所有客户*/ List<Customer> getAllCustomers(); /*根据id删除客户*/ public void deleteCustomer(Integer id); }
CustomerMapper.xml
<delete id="deleteCustomer"> DELETE FROM `customer` WHERE cust_id = #{id} </delete>
OrderMapper
public interface OrderMapper { /*查询所有的订单*/ List<Order> getAllOrders(); /*根据id查询订单*/ Order getOrderbyId(Integer id); /*保存订单*/ void insertOrder(Order order); /*根据cust_id查询订单*/ Order getOrderbyCustId(Integer id); /*更新与客户的关系*/ void updateCustId(@Param("orderId") Integer orderId, @Param("custId") Integer custId); /*打破跟客户关系*/ void updateRelation(Integer custId); }
OrderMapper.xml
<update id="updateRelation"> UPDATE `order` SET cust_id = NULL WHERE cust_id = #{custId} </update>
测试
@Test public void test6(){ SqlSession sqlSession = MybatisUtils.openSession(); CustomerMapper customerMapper = sqlSession.getMapper(CustomerMapper.class); OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class); // 一对多删除之前 要先打破关系 orderMapper.updateRelation(17); /*删除客户*/ customerMapper.deleteCustomer(17); sqlSession.commit(); sqlSession.close(); }
5. ManyToMany 多对多
一个老师教多个学生,一个学生可以被多个老师教。
(0)建表和建实体类
SET FOREIGN_KEY_CHECKS=0; -- ---------------------------- -- 学生表 -- ---------------------------- DROP TABLE IF EXISTS `student`; CREATE TABLE `student` ( `stu_id` int(11) NOT NULL AUTO_INCREMENT, `stu_name` varchar(255) DEFAULT NULL, PRIMARY KEY (`stu_id`) ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of student -- ---------------------------- INSERT INTO `student` VALUES ('1', '学生1'); INSERT INTO `student` VALUES ('2', '学生2'); INSERT INTO `student` VALUES ('3', '学生3'); INSERT INTO `student` VALUES ('4', '学生4'); INSERT INTO `student` VALUES ('5', '学生5'); -- ---------------------------- -- 老师表 -- ---------------------------- DROP TABLE IF EXISTS `teacher`; CREATE TABLE `teacher` ( `teacher_id` int(11) NOT NULL AUTO_INCREMENT, `teacher_name` varchar(255) DEFAULT NULL, PRIMARY KEY (`teacher_id`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of teacher -- ---------------------------- INSERT INTO `teacher` VALUES ('1', '高老师'); INSERT INTO `teacher` VALUES ('2', '叶老师'); INSERT INTO `teacher` VALUES ('3', '王老师'); -- ---------------------------- -- 学生老师关系表 -- ---------------------------- DROP TABLE IF EXISTS `stu_teacher_rel`; CREATE TABLE `stu_teacher_rel` ( `stu_id` int(11) NOT NULL, `teacher_id` int(11) NOT NULL, PRIMARY KEY (`stu_id`,`teacher_id`), KEY `fk_teacher_rel` (`teacher_id`), CONSTRAINT `fk_stu_rel` FOREIGN KEY (`stu_id`) REFERENCES `student` (`stu_id`), CONSTRAINT `fk_teacher_rel` FOREIGN KEY (`teacher_id`) REFERENCES `teacher` (`teacher_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of stu_teacher_rel -- ---------------------------- INSERT INTO `stu_teacher_rel` VALUES ('1', '1'); INSERT INTO `stu_teacher_rel` VALUES ('2', '1'); INSERT INTO `stu_teacher_rel` VALUES ('1', '2'); INSERT INTO `stu_teacher_rel` VALUES ('3', '2'); INSERT INTO `stu_teacher_rel` VALUES ('4', '2'); INSERT INTO `stu_teacher_rel` VALUES ('5', '3');
Student
@Setter@Getter @ToString public class Student { private Integer stu_id; private String stu_name; }
Teacher
@Setter@Getter@ToString public class Teacher { private Integer teacher_id; private String teacher_name; }
(1)查询
Ⅰ)左连接查询
Teacher
@Setter@Getter@ToString public class Teacher { private Integer teacher_id; private String teacher_name; private List<Student> students = new ArrayList<>(); }
TeacherMapper
public interface TeacherMapper { /*查询老师 并且把关联的学生也查出来*/ public List<Teacher> getAllTeachers(); }
TeacherMapper.xml
<select id="getAllTeachers" resultMap="teacherMap"> SELECT*FROM teacher as t LEFT JOIN stu_teacher_rel as r on t.teacher_id = r.teacher_id LEFT JOIN student as s ON r.stu_id = s.stu_id </select> <resultMap id="teacherMap" type="Teacher"> <id column="teacher_id" property="teacher_id"/> <result column="teacher_name" property="teacher_name"/> <collection property="students" javaType="list" ofType="Student"> <id column="stu_id" property="stu_id"/> <result column="stu_name" property="stu_name"/> </collection> </resultMap>
测试
@Test public void test(){ SqlSession sqlSession = MybatisUtils.openSession(); TeacherMapper teacherMapper = sqlSession.getMapper(TeacherMapper.class); List<Teacher> allTeachers = teacherMapper.getAllTeachers(); for (Teacher teacher : allTeachers) { System.out.println(teacher); } sqlSession.close(); }
Ⅱ)分步查询
TeacherMapper
public interface TeacherMapper { /*查询老师 并且把关联的学生也查出来*/ public List<Teacher> getAllTeachers(); /*查询指定的老师*/ public Teacher getTeacherWithId(Integer id); }
TeacherMapper.xml
<resultMap id="teacherMap2" type="Teacher"> <id column="teacher_id" property="teacher_id"/> <result column="teacher_name" property="teacher_name"/> <collection property="students" ofType="Student" select="com.mybatis.mapper.StudentMapper.getStuByTeach" column="teacher_id"/> </resultMap> <select id="getTeacherWithId" resultMap="teacherMap2"> SELECT * from teacher WHERE teacher_id = #{id}; </select>
StudentMapper
public interface StudentMapper { /*根据老师id查询学生*/ public List<Student> getStuByTeach(Integer id); }
StudentMapper.xml
<select id="getStuByTeach" resultType="Student"> SELECT * from student where stu_id in( SELECT stu_id from stu_teacher_rel where teacher_id = #{id}) </select>
测试
@Test public void test2(){ SqlSession sqlSession = MybatisUtils.openSession(); TeacherMapper teacherMapper = sqlSession.getMapper(TeacherMapper.class); Teacher teacher = teacherMapper.getTeacherWithId(2); System.out.println(teacher); sqlSession.close(); }
(2)添加
添加老师,添加学生,添加中间关系
TeacherMapper
public interface TeacherMapper { /*查询老师 并且把关联的学生也查出来*/ public List<Teacher> getAllTeachers(); /*查询指定的老师*/ public Teacher getTeacherWithId(Integer id); /*保存老师*/ void insertTeacher(Teacher teacher); /*插入关系表*/ void insertRelation(@Param("stuId") Integer stuId, @Param("teacherId") Integer teacherId); }
TeacherMapper.xml
<!--保存老师--> <insert id="insertTeacher" parameterType="Teacher" useGeneratedKeys="true" keyProperty="teacher_id" keyColumn="teacher_id"> INSERT INTO `teacher` (teacher_name) VALUES (#{teacher_name}) </insert> <!--插入关系表--> <insert id="insertRelation"> INSERT INTO stu_teacher_rel (stu_id, teacher_id) VALUES (#{stuId}, #{teacherId}) </insert>
StudentMapper
public interface StudentMapper { /*根据老师id查询学生*/ public List<Student> getStuByTeach(Integer id); /*保存学生*/ void insertStudent(Student student1); }
StudentMapper.xml
<!--保存学生--> <insert id="insertStudent" parameterType="Student" useGeneratedKeys="true" keyProperty="stu_id" keyColumn="stu_id"> INSERT INTO `student` (stu_name) VALUES (#{stu_name}) </insert>
测试
@Test public void test3(){ SqlSession sqlSession = MybatisUtils.openSession(); TeacherMapper teacherMapper = sqlSession.getMapper(TeacherMapper.class); StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class); Teacher teacher = new Teacher(); teacher.setTeacher_name("新老师"); Student student1 = new Student(); student1.setStu_name("新学生2"); Student student2 = new Student(); student2.setStu_name("新学生2"); teacher.getStudents().add(student1); teacher.getStudents().add(student2); /*保存老师*/ teacherMapper.insertTeacher(teacher); /*保存学生*/ studentMapper.insertStudent(student1); studentMapper.insertStudent(student2); /*插入关系表*/ for (Student student : teacher.getStudents()) { teacherMapper.insertRelation(student.getStu_id(),teacher.getTeacher_id()); } sqlSession.commit(); sqlSession.close(); }
四、动态sql
1. 什么是动态sql
通过mybatis提供的各种标签方法实现动态拼接sql。
2. if 标签
需求:根据客户名称和职业来查询客户
public interface CustomerMapper { /*根据客户名称和职业来查询*/ List<Customer> getCustomer (@Param("name") String name, @Param("profession") String profession); }
CustomerMapper.xml
<select id="getCustomer" resultType="Customer"> SELECT * FROM `customer` WHERE `cust_name` = #{name} AND `cust_profession` = #{profession} </select>
测试
@Test public void test(){ SqlSession sqlSession = MybatisUtils.openSession(); CustomerMapper customerMapper = sqlSession.getMapper(CustomerMapper.class); List<Customer> customers = customerMapper.getCustomer("李白","刺客"); for (Customer customer : customers) { System.out.println(customer); } sqlSession.close(); }
原始查询存在问题
有可能传入的名称或职业为空,那时就找不到记录了,可以使用 if 标签来进行判断。
CustomerMapper.xml
<select id="getCustomer" resultType="Customer"> SELECT * FROM `customer` WHERE <if test="name != null and name != ''"> `cust_name` = #{name} </if> <if test="profession != null and profession!=''"> AND `cust_profession` = #{profession} </if> </select>
这样,能够解决:
① List<Customer> customers = customerMapper.getCustomer("李白","刺客");
Preparing: SELECT * FROM customer
WHERE cust_name
= ? AND cust_profession
= ?
Parameters: 李白(String), 刺客(String)
② List<Customer> customers = customerMapper.getCustomer("李白",null);
Preparing: SELECT * FROM customer
WHERE cust_name
= ?
Parameters: 李白(String)
存在的问题
① List<Customer> customers = customerMapper.getCustomer(null,"刺客");
Preparing: SELECT * FROM customer
WHERE AND cust_profession
= ?
Parameters: 刺客(String)
如果第一个条件为null,后面就会多一个and执行就会报错
② List<Customer> customers = customerMapper.getCustomer(null,null);
Preparing: SELECT * FROM customer
WHERE
如果所有条件为null,后面就会多一个WHERE执行就会报错
3. Where 标签
解决 if 标签留下的两个问题
① 会自动去掉 where 后面第一个前And
② 如果没有条件就不会加 where
<select id="getCustomer" resultType="Customer"> SELECT * FROM `customer` <where> <if test="name != null and name != ''"> `cust_name` = #{name} </if> <if test="profession != null and profession!=''"> AND `cust_profession` = #{profession} </if> </where> </select>
4. trim 标签
① prefix:设置前缀,在第一个条件之前加一个前缀。
② prefixOverrides:前缀条件覆盖,把第一个条件之前的值覆盖(变成空)
③ suffix:设置后缀,在最后一个条件之后加一个后缀。
④ suffixOverrides::后缀条件覆盖,把最后一个条件之后的值覆盖(变成空)
<select id="getCustomer" resultType="Customer"> select * from `customer` <trim prefix="where" prefixOverrides="and" suffixOverrides="and" > <if test="name != null and name != ''"> and `cust_name` = #{name} </if> <if test="profession != null and profession!=''"> and `cust_profession` = #{profession} and </if> </trim> </select>
5. choose 标签
① choose:选择一个条件,只要第一个条件满足,后面条件都不执行
② when:写条件
③ otherwise:除此以外
<select id="getCustomer" resultType="Customer"> select * from `customer` <where> <choose> <when test="profession != null and profession!=''"> `cust_profession`=#{profession} </when> <when test="name != null and name != ''"> `cust_name`=#{name} </when> <otherwise>1=1</otherwise> </choose> </where> </select>
6. set 标签
① 会自动去掉最后一个设置的逗号
② 会添加update语句中 set
CustomerMapper
public interface CustomerMapper { /*根基客户名称和职业来查询*/ List<Customer> getCustomer (@Param("name") String name, @Param("profession") String profession); /*更新客户*/ public void updateCustomer(Customer customer); }
CustomerMapper.xml
<update id="updateCustomer"> update `customer` <set> <if test="cust_name != null and cust_name !='' "> cust_name=#{cust_name}, </if> <if test="cust_profession != null and cust_profession !='' "> cust_profession=#{cust_profession}, </if> </set> where cust_id = #{cust_id} </update>
测试
@Test public void test2(){ SqlSession sqlSession = MybatisUtils.openSession(); CustomerMapper customerMapper = sqlSession.getMapper(CustomerMapper.class); Customer customer = new Customer(); customer.setCust_id(2); customer.setCust_name("李白白"); customer.setCust_profession("战士"); customerMapper.updateCustomer(customer); sqlSession.commit(); sqlSession.close(); }
7. foreach 标签
查询条件值为指定的值当中
public interface CustomerMapper { /*根据id查询指定的客户 多个客户*/ List<Customer> getCustomers(); }
CustomerMapper.xml
<select id="getCustomers" resultType="Customer"> select * from `customer` where `cust_id` in (2,3,4,5,6); </select>
测试
@Test public void test3(){ SqlSession sqlSession = MybatisUtils.openSession(); CustomerMapper customerMapper = sqlSession.getMapper(CustomerMapper.class); List<Customer> customers = customerMapper.getCustomers(); for (Customer customer : customers) { System.out.println(customer); } sqlSession.close(); }
给定的值可以以三种形式给出
(1)数组
查询条件值为指定的值当中
public interface CustomerMapper { /*根据id查询指定的客户 多个客户*/ List<Customer> getCustomers(Integer[] ids); }
CustomerMapper.xml
<select id="getCustomers" parameterType="Integer[]" resultType="Customer"> select * from `customer` where `cust_id` in <foreach collection="array" open="(" close=")" separator="," item="id"> #{id} </foreach> </select>
测试
@Test public void test3(){ SqlSession sqlSession = MybatisUtils.openSession(); CustomerMapper customerMapper = sqlSession.getMapper(CustomerMapper.class); List<Customer> customers = customerMapper.getCustomers(new Integer[]{2,3,4,5}); for (Customer customer : customers) { System.out.println(customer); } sqlSession.close(); }
(2)List
查询条件值为指定的值当中
public interface CustomerMapper { /*根据id查询指定的客户 多个客户*/ List<Customer> getCustomers(List<Integer> ids); }
CustomerMapper.xml
<select id="getCustomers" parameterType="List" resultType="Customer"> select * from `customer` where `cust_id` in <foreach collection="list" open="(" close=")" separator="," item="id"> #{id} </foreach> </select>
测试
@Test public void test3(){ SqlSession sqlSession = MybatisUtils.openSession(); CustomerMapper customerMapper = sqlSession.getMapper(CustomerMapper.class); ArrayList<Integer> arrayList = new ArrayList<>(); arrayList.add(2); arrayList.add(3); arrayList.add(4); arrayList.add(6); List<Customer> customers = customerMapper.getCustomers(arrayList); for (Customer customer : customers) { System.out.println(customer); } sqlSession.close(); }
(3)VO
创建 VO (VO = Value Object 数据包装类)
@Setter@Getter public class QueryVo { private Integer[] ids; private List<Integer> idList; }
CustomerMapper
public interface CustomerMapper { /*根据id查询指定的客户 多个客户*/ List<Customer> getCustomers(QueryVo queryVo); }
CustomerMapper.xml(第一种)
<select id="getCustomers" parameterType="QueryVo" resultType="Customer"> select * from `customer` where `cust_id` in <foreach collection="ids" open="(" close=")" separator="," item="id"> #{id} </foreach> </select>
CustomerMapper.xml(第二种)
<select id="getCustomers" parameterType="QueryVo" resultType="Customer"> select * from `customer` where `cust_id` in <foreach collection="idList" open="(" close=")" separator="," item="id"> #{id} </foreach> </select>
测试(第一种)
@Test public void test3(){ SqlSession sqlSession = MybatisUtils.openSession(); CustomerMapper customerMapper = sqlSession.getMapper(CustomerMapper.class); QueryVo queryVo = new QueryVo(); queryVo.setIds(new Integer[]{2,3,4,5}); List<Customer> customers = customerMapper.getCustomers(queryVo); for (Customer customer : customers) { System.out.println(customer); } sqlSession.close(); }
测试(第二种)
@Test public void test3(){ SqlSession sqlSession = MybatisUtils.openSession(); CustomerMapper customerMapper = sqlSession.getMapper(CustomerMapper.class); QueryVo queryVo = new QueryVo(); ArrayList<Integer> arrayList = new ArrayList<>(); arrayList.add(2); arrayList.add(3); arrayList.add(4); arrayList.add(6); queryVo.setIdList(arrayList); List<Customer> customers = customerMapper.getCustomers(queryVo); for (Customer customer : customers) { System.out.println(customer); } sqlSession.close(); }
8. bind 标签
bind标签:声明一个变量,绑定值;可以取出传入的值,重新处理,赋值给别外一个值
public interface CustomerMapper { Customer getCustomerbyId(@Param("id") Integer id); }
① 声明一个变量,绑定值
CustomerMapper.xml
<select id="getCustomerbyId" resultType="Customer"> <bind name="id" value="6"></bind> select * from `customer` where `cust_id` = #{id} </select>
测试
@Test public void test4(){ SqlSession sqlSession = MybatisUtils.openSession(); CustomerMapper customerMapper = sqlSession.getMapper(CustomerMapper.class); Customer customer = customerMapper.getCustomerbyId(3); System.out.println(customer); sqlSession.close(); }
之后查询结果是查id为6的记录。即查询的参数是以bind标签绑定的数据为准。
② 可以取出传入的值,重新处理,赋值给别外一个值
CustomerMapper.xml
<select id="getCustomerbyId" resultType="Customer"> <bind name="id" value="id+2"></bind> select * from `customer` where `cust_id` = #{id} </select>
测试同上,查询结果是查id为5的记录。
9. Sql 片段
Sql中可将重复的sql提取出来,使用时用include引用即可,最终达到sql重用的目的。
sql 片段中还能使用动态标签
<select id="getCustomerbyId" resultType="Customer"> <include refid="selectID"/> where `cust_id` = #{id} </select> <sql id="selectID"> <choose> <when test="id == 2"> select cust_name from `customer` </when> <otherwise> select * from `customer` </otherwise> </choose> </sql>
include 中还能添加属性
<select id="getCustomerbyId" resultType="Customer"> <include refid="selectID"> <property name="id" value="3"/> </include> where `cust_id` = #{id} </select> <!--注意 在include当中定义的property 取的时候 要使用${} --> <sql id="selectID"> <choose> <when test="${id} == 3"> select cust_profession from `customer` </when> <when test="id == 2"> select cust_name from `customer` </when> <otherwise> select * from `customer` </otherwise> </choose> </sql>