一.基本认识
- mybatis是一个ORM框架
- ORM:对象关系映射
- mybatis对jdbc的重复代码进行了封装,依然需要自己写sql(比jdbc开发快,比jpa性能好)
二.hello,mybatis
2.1 导包
- mybatis核心包
mybatis/mybatis-3.2.1.jar
- mybatis的依赖包
mybatis/lib/*.jar
- jdbc驱动包
mysql-connector-java-5.1.26-bin.jar
2.2 基本准备(表,domain)
- 库(mybatis) -> 表(product)【导入-resources\准备\sql脚本\product.sql】
- Product对象
public class Product {
private Long id; //产品名称 private String productName; //成本价 private Double costPrice; //销售价格 private Double salePrice; //供应商 private String supplier; //品牌 private String brand; //折扣 private Double cutoff; //产品类型 private Long dir_id; //省略getter,setter与toString... }
2.3 创建配置文件
配置文件的位置要注意
- 注:配置文件的代码可以去找:网上,官方文档,官方中文文档
- 官方中文文档 : doc/mybatis-3.2.1.pdf
jdbc.properties:数据库的参数
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///mybatis
jdbc.username=root
jdbc.password=123456
mybatis-config.xml:mybatis的核心配置
<?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:配置(xml的一个根)--> <configuration> <!--引入jdbc.propeties文件--> <properties resource="jdbc.properties" /> <!-- environments:环境(多个环境) default="development":多个环境中默认使用的是哪一个环境 --> <environments default="development"> <!-- environment:某一个环境 id:就是这个环境的名称 --> <environment id="development"> <!-- transactionManager:事务管理(ACID) type="JDBC|MANAGED" jdbc:简单jdbc事务 MANAGED:啥都不做 --> <transactionManager type="JDBC"/> <!-- 数据源(连接池) POOLED:mybatis内置的连接池 --> <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> <!--引入(找到)写SQL的XML--> <mapper resource="cn/itsource/dao/ProductMapper.xml"/> </mappers> </configuration>
ProductMapper.xml: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:根(每个xml都得有,不用管它的含义) namespace:命名空间(随便取个名称) --> <mapper namespace="cn.itsource.dao.ProductMapper"> <!-- select:代表这是一个查询语句 id:代表这个查询语句的唯一命名 以后你要找到这条SQL: namespace+id 例:cn.itsource.dao.ProductMapper.getOne parameterType:参数类型 long -> Long _long -> long resultType:返回的每一条结果的类型 注:返回类型的全限定名 --> <select id="getOne" parameterType="long" resultType="cn.itsource.domain.Product"> select * from product where id = #{id} </select> </mapper>
2.4 测试
- 读取核心配置文件
- 获取SqlSession对象
SqlSessionFactoryBuilder -> SqlSessionFactory -> SqlSession
- 执行查询
/**
* ①. 想办法搞到核心配置文件 mybatis-config.xml
* ②. 获取到它的核心对象 SqlSession【相当于是咱们JPA中的EntityManager对象】 * EntityManagerFactory -> EntityManager * SqlSessionFactoryBuilder -> SqlSessionFactory -> SqlSession * ③.SqlSession就可以CRUD.. */ @Test public void testHello() throws Exception{ //①. 想办法搞到核心配置文件 mybatis-config.xml Reader reader = Resources.getResourceAsReader("mybatis-config.xml"); //②.获取到它的核心对象 SqlSession // 2.1获到到SqlSessionFactory对象 SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); SqlSessionFactory factory = builder.build(reader); // 2.2获取到它的核心对象 SqlSession SqlSession session = factory.openSession(); //③.获取到一个对象 /** * String statement : 获取SQL的字符串 namespace+id * Object parameter : 参数传递 */ Product product = session.selectOne("cn.itsource.dao.ProductMapper.getOne",1L); System.out.println(product); //关闭session session.close(); }
三.工具类与CRUD
3.1 准备MyBatisUtil
- SqlSessionFactoryBuilder:只用于创建factory,用完就可以扔掉
- SqlSessionFactory:重级量对象,创建后不要随便销毁(一个项目一个这个对象即可)
- SqlSession :用于完成咱们的CRUD
/**
* MyBatis的工具类,主要就是为我们创建SqlSession对象
*/
public class MyBatisUtil { //定义一个SqlSessionFactory对象 /** * 一个对象写这个位置,而且是静态的,它是有线程安全问题的 */ private static SqlSessionFactory factory = null; /** * SqlSessionFactoryBuilder:唯一的作用就是创建SqlSessionFactory * 一旦用完就可以把它把抛弃了 * 1.类加载的时候就会执行 * 2.只会执行一次 */ static { try { factory = new SqlSessionFactoryBuilder().build( Resources.getResourceAsReader("mybatis-config.xml") ); } catch (Exception e) { e.printStackTrace(); } } public static SqlSession openSession(){ return factory.openSession(); } }
3.1 完成CRUD
3.1.1 productMapper.xml
<!--
select:代表这是一个查询语句
id:代表这个查询语句的唯一命名
以后你要找到这条SQL: namespace+id 例:cn.itsource.dao.ProductMapper.getOne parameterType:参数类型 long -> Long _long -> long resultType:返回的每一条结果的类型 注:返回类型的全限定名 --> <select id="getOne" parameterType="long" resultType="product"> select * from product where id = #{id} </select> <!--resultType:返回的每一条结果的类型--> <select id="getAll" resultType="product" > select * from product </select> <!-- 添加一条数据 useGeneratedKeys:是否要主键 keyColumn="id":在数据库中名称叫id keyProperty="id":在类中也叫id 主键会放到你传过来的对象中 --> <insert id="save" parameterType="product" useGeneratedKeys="true" keyColumn="id" keyProperty="id"> insert into product (productName,dir_id,salePrice,supplier,brand,cutoff,costPrice) values (#{productName},#{dir_id},#{salePrice},#{supplier},#{brand},#{cutoff},#{costPrice}) </insert> <!--修改功能--> <update id="update" parameterType="product"> update product set productName=#{productName}, dir_id=#{dir_id}, salePrice=#{salePrice}, supplier=#{supplier}, brand=#{brand}, cutoff=#{cutoff}, costPrice=#{costPrice} where id=#{id} </update> <!-- 删除功能 --> <delete id="delete" parameterType="long"> delete from product where id=#{id} </delete>
3.1.1 productDao中完成功能
- 增删改需要提交事务
package cn.itsource.dao.impl;
import cn.itsource.dao.IProductDao;
import cn.itsource.domain.Product;
import cn.itsource.util.MyBatisUtil;
import org.apache.ibatis.session.SqlSession; import java.util.List; public class ProductDaoImpl implements IProductDao { private final String NAMESPACE = "cn.itsource.dao.ProductMapper."; @Override public void save(Product product) { SqlSession session = null; try { session = MyBatisUtil.openSession(); session.insert(NAMESPACE+"save",product); //提交事务 session.commit(); } catch (Exception e) { //回滚事务 session.rollback(); e.printStackTrace(); } finally { session.close(); } } @Override public void update(Product product) { SqlSession session = null; try { session = MyBatisUtil.openSession(); session.update(NAMESPACE+"update",product); //提交事务 session.commit(); } catch (Exception e) { //回滚事务 session.rollback(); e.printStackTrace(); } finally { session.close(); } } @Override public void delete(Long id) { SqlSession session = null; try { session = MyBatisUtil.openSession(); session.delete(NAMESPACE+"delete",id); //提交事务 session.commit(); } catch (Exception e) { //回滚事务 session.rollback(); e.printStackTrace(); } finally { session.close(); } } @Override public Product getOne(Long id) { SqlSession session = null; try { session = MyBatisUtil.openSession(); return session.selectOne(NAMESPACE+"getOne",id); } catch (Exception e) { e.printStackTrace(); } finally { session.close(); } return null; } @Override public List<Product> getAll() { SqlSession session = null; try { session = MyBatisUtil.openSession(); return session.selectList(NAMESPACE+"getAll"); } catch (Exception e) { e.printStackTrace(); } finally { session.close(); } return null; } }
四.细节处理
4.1 添加的对象返回id
- useGeneratedKeys:是否要使用主键
- keyColumn="id":在数据库中名称叫id
- keyProperty="id":在类中主键的名称id
- 主键会放到你传过来的对象中
<!--
添加一条数据
-->
<insert id="save" parameterType="product" useGeneratedKeys="true" keyColumn="id" keyProperty="id"> insert into product (productName,dir_id,salePrice,supplier,brand,cutoff,costPrice) values (#{productName},#{dir_id},#{salePrice},#{supplier},#{brand},#{cutoff},#{costPrice}) </insert>
4.2 别名的配置
- 内置别名(int,long,..)/自定义别名(自己的类)
- 别名不区分大小写
- 自定义别名的配置
- 每个对象单独配置别名
<typeAlias type="cn.itsource.domain.Product" alias="Product" />
- 扫描包(这个包的对象都会取别名)
<package name="cn.itsource.domain" />
- 每个对象单独配置别名
<?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:配置(xml的一个根)--> <configuration> <!--引入jdbc.propeties文件--> <properties resource="jdbc.properties" /> <!-- typeAliases:配置别名 注:别名不区别大小写 --> <typeAliases> <!-- type:类型 alias:别名--> <!--<typeAlias type="cn.itsource.domain.Product" alias="Product" />--> <!--为这个包下面的所有类取别名(就是类名)--> <package name="cn.itsource.domain" /> <package name="cn.itsource.query" /> </typeAliases> <environments default="development"> ... </environments> <mappers> ... </mappers> </configuration>
4.3 日志的处理
- mybatis中是使用日志来显示sql的
- 在资源根目录创建一个log4j.properties
# ERROR错误的日志 WARN:警告 INFO:普通信息 DEBUG:调试日志 TRACE:日志 log4j.rootLogger=ERROR, stdout #log4j.rootLogger=NONE #把左边包名改成你自己的包名 log4j.logger.cn.itsource=TRACE # 日志打印到控制台中 log4j.appender.stdout=org.apache.log4j.ConsoleAppender # 日志打印的一种格式(可以灵活地指定布局模式) log4j.appender.stdout.layout=org.apache.log4j.PatternLayout # 日志打印的格式是什么样子的 %d:日期 %p:优先级 %c:类的全名 %m:输出的结果 %n:换行 log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n
4.4 #与$的区别(面试题)
- $只能从对象中取属性(getter属性)
- #支持预编译方案(性能更好,安全性更高)
- 优先使用#,有些拼接字符串的地方不支持,再使用$
五.批量操作与动态SQL
5.1 批量添加
- sql:
insert into 表名 (列名,列名) values (?,?),(?,?),...
- 实现使用foreach
<!--
批量添加
insert into employee (name,age,sex) values ("小小良",45,false),("大二良",45,true) foreach:循环 collection:遍历的集合 item:每次遍历拿到的对象 separator:分隔符(每个值都使用,隔开) --> <insert id="batchSave" parameterType="list"> insert into employee (name,age,sex) values <foreach collection="list" item="e" separator=","> (#{e.name},#{e.age},#{e.sex}) </foreach> </insert>
5.2 批量删除
- sql:
delete from 表名 where id in(?,?,..)
- 实现使用foreach
<!--
批量删除:DELETE FROM employee WHERE id in (?,?,?,...)
传 : List<Long> ids
foreach:循环 collection:遍历的集合 item:每次遍历拿到的对象 separator:分隔符(每个值都使用,隔开) open:以什么开头 close:以什么结尾 index:遍历的索引 --> <delete id="batchDelete" parameterType="list"> delete from employee where id in <foreach collection="list" item="id" separator="," open="(" close=")"> #{id} </foreach> </delete>
5.3 动态修改
- 解决咱们以前的数据丢失问题
- 对象中没有值就不做修改
<!--
动态修改:只修改有值的数据
-->
<update id="dynamicUpdate" parameterType="employee"> update employee <set> <if test="name!=null"> name=#{name}, </if> <if test="age!=null"> age=#{age}, </if> <if test="sex!=null"> sex=#{sex}, </if> </set> where id=#{id} </update>
今日扩展
连接池
比较流行的连接池有三个
- Spring建议使用的DBCP
- Hibernate建议使用的C3P0
- 阿里连接池 druid -> 现在外面用得还是比较多
mysql的数据库引擎
- InnoDB : 支持事务,支持外键
- MyISAM : 不支持事务,不支持外键(速度快)