MyBatis:MyBatis基础,动态SQL,关联查询,MyBatis一二级缓存机制,ehcache,MyBatis整合Spring、SpringMVC,MyBatis****,MyBatis插件
1 MyBatis简介
1 简介
MyBatis半自动化的ORM框架
Hibernate全自动的ORM框架
ORM:Object(JavaBean) Relation(关系:和数据库记录的关系)Mapping(数据库的记录和JavaBean对应)
1 HelloWorld(MyBatis操作数据库的基本环节)
1 创建数据表
2 JavaBean类
3 编写dao接口
4 MyBatis操作数据库★
1)导包
mybatis-3.2.8.jar
mysql-connector-java-5.1.37-bin.jar//数据库驱动
log4j.jar
注:log4j依赖配置文件。 这个配置文件可以是xml,也可以是properties文件。但是要求名字必须是log4j。必须放在类路径下(直接放在源码包下)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
<param name="Encoding" value="UTF-8" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%-5p %d{MM-dd HH:mm:ss,SSS} %m (%F:%L) \n" />
</layout>
</appender>
<logger name="java.sql">
<level value="debug" />
</logger>
<logger name="org.apache.ibatis">
<level value="debug" />
</logger>
<root>
<level value="debug" />
<appender-ref ref="STDOUT" />
</root>
</log4j:configuration>
2)编写myBatis全局配置文件
这个配置文件的作用就是用来指导MyBatis如何正常工作。Xml配置文件中包含了对MyBatis系统的核心设置,包含获取数据库连接实例的数据源(DataSource)和决定事务范围和控制方式的事务管理器(TransactionManager)。从官方文档复制
注意:如果编写文件时没有提示,就需要导入约束文件。约束文件的URI地址:http://mybatis.org/dtd/mybatis-3-config.dtd。导入步骤:window-perference-xml-xml Catalog
【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">
<!-- dtd文件是xml文档的约束文件。这个约束文件就是规定这个xml能写什么标签的。 有个这个约束就知道能写什么? 只要这个约束文件的位置被正确指向,就会有提示。 给eclipse配置http://mybatis.org/dtd/mybatis-3-config.dtd约束文件的所在位置 在jar包中的org.apache.ibatis.builder.xml下有,直接拿出来 -->
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<!-- 配置数据源。使用连接池技术。连接池使用mybatis自己内置的连接池 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql:///person" />
<property name="username" value="root" />
<property name="password" value="123456" />
</dataSource>
</environment>
</environments>
<!-- 一定要把我们写好的SQL映射配置文件 ,注册进MyBatis全局配置中 -->
<mappers>
<!-- 每一个 mapper标签就是注册一个配置文件:resource:是指定从类路径下开始的一个文件路径 -->
<mapper resource="mybatis/mapper/PersonDao.xml"/>
</mappers>
</configuration>
- 编写SQL映射文件。
这个文件的作用就是PersonDao的实现文件,相当于PersonDaoImpl实现类。从官方文档复制
注:这个配置文件要有提示,也需要导入约束文件。约束文件的URI:http://mybatis.org/dtd/mybatis-3-mapper.dtd
【编写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">
<!-- 这个文件的作用就是相当于对PersonDao的每一个方法的实现namespace:必须是接口的全类名,这样MyBatis才知道这个配置文件是相当于对哪个接口的实现 -->
<mapper namespace="dao.PersonDao">
<!-- public void savePerson(Person person); -->
<!--定义一个插入操作id="":一定要是方法名parameterType:指定这个方法的传入的参数类型:写类型全类名 -->
<insert id="savePerson" parameterType="bean.Person">
<!-- 标签体内直接写SQL语句即可,SQL的分号一般不要携带 #{属性名}:取出传入的对象的这个属性的值-->
INSERT INTO tal_person(NAME,age,birth,registerTime,salary)
VALUES(#{name},#{age},#{birth},#{registerTime},#{salary})
</insert>
<update id="updatePerson" parameterType="bean.Person">
UPDATE tal_person SET
NAME=#{name},age=#{age},birth=#{birth},
registerTime=#{registerTime},salary=#{salary}
WHERE id=#{id}
</update>
<delete id="deletePerson" parameterType="Integer">
DELETE FROM tal_person WHERE id=#{id}
</delete>
<select id="getPersonById" parameterType="Integer" resultType="bean.Person">
SELECT * FROM tal_person WHERE id=#{id}
</select>
<select id="getAll" resultType="bean.Person">
SELECT * FROM tal_person
</select>
</mapper>
5 测试
public class MyBatisTest {
SqlSessionFactory sessionFactory = null;
//该方法在所有单元测试方法,前运行
@Before
public void getSession() throws IOException {
String resource = "mybatis-config.xml";
// 从类路径下获取这个文件的流
InputStream stream = Resources.getResourceAsStream(resource);
// 根据配置文件的流,使用SqlSessionFactory的建造器创建一个sqlsessionFactory对象
sessionFactory = new SqlSessionFactoryBuilder().build(stream);
}
// 根据id获取
@Test
public void testDelete() {
// 打开一次数据库连接的会话
SqlSession session = sessionFactory.openSession();
try {
// 执行更新操作
// 获取映射
PersonDao personDao = session.getMapper(PersonDao.class);
personDao.deletePerson(3);
session.commit();
} finally {
session.close();
}
}
// 获取所有
@Test
public void testGetAll() {
// 打开一次数据库连接的会话
SqlSession session = sessionFactory.openSession();
try {
// 执行更新操作
// 获取映射
PersonDao personDao = session.getMapper(PersonDao.class);
List<Person> all = personDao.getAll();
for (Person person : all) {
System.out.println(person);
}
session.commit();
} finally {
session.close();
}
}
// 根据id获取
@Test
public void testQueryById() {
// 打开一次数据库连接的会话
SqlSession session = sessionFactory.openSession();
try {
// 执行更新操作
// 获取映射
PersonDao personDao = session.getMapper(PersonDao.class);
Person person = personDao.getPersonById(2);
System.out.println(person);
session.commit();
} finally {
session.close();
}
}
// 更新操作
@Test
public void testUpdate() {
// 打开一次数据库连接的会话
SqlSession session = sessionFactory.openSession();
try {
// 执行更新操作
// 获取映射
PersonDao personDao = session.getMapper(PersonDao.class);
Person person = personDao.getPersonById(2);
person.setName("大圣归来");
personDao.updatePerson(person);
session.commit();
} finally {
session.close();
}
}
@Test
public void test() throws IOException {
// 3、SqlSessionFactory:一定是用来创建SqlSession的
// SqlSession:和数据库的一次会话。类似于我们的Connection;
// 4、使用SqlSessionFactory开启和数据库的一次会话
SqlSession session = sessionFactory.openSession();
// 5、SqlSession可以完成完整的增删改查
// 6、获取到接口是动态代理
try {
PersonDao dao = session.getMapper(PersonDao.class);
Person person = new Person();
person.setAge(20);
person.setName("张三丰");
person.setSalary(298.98);
person.setBirth(new Date());
// 7、保存
dao.savePerson(person);
// 8、提交会话,增删改才能生效
session.commit();
} catch (Exception e) {
e.printStackTrace();
} finally {
// 9、关闭会话。
session.close();
}
}
}
2 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>
<!--标签之间的先后顺序一定要注意Content Model : (properties?, settings?, typeAliases?, typeHandlers?, objectFactory?, objectWrapperFactory?, plugins?, environments?, databaseIdProvider?, mappers?) -->
<!-- 这个配置文件指导MyBatis正常执行 -->
<!-- 1、这个标签是用来进行属性声明或者引用 resource:指定要引用的配置文件(properties)的资源文件位置。从类路径下 url:如果我们配置文件需要请求一个网络地址:http://www.atguigu.com/mybatis/xx.xml 如果属性在不只一个地方进行了配置,那么 MyBatis 将按照下面的顺序来加载: 1)、在 properties 元素体内指定的属性首先被读取。
2)、然后根据 properties 元素中的 resource 属性读取类路径下属性文件或根据 url 属性指定的路径读取属性文件,并覆盖已读取的同名属性。
3)、最后读取作为方法参数传递的属性,并覆盖已读取的同名属性。 标签体内的property优先级最低==》配置文件的内容==》方法的参数 -->
<properties resource="dbconfig.properties">
<!--声明一些变量 -->
<property name="username" value="root123" />
</properties>
<!-- 这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。 -->
<settings>
<!-- 关闭了全局缓存 -->
<setting name="cacheEnabled" value="false" />
</settings>
<!--别名处理器: MyBatis运行为我们引用的javaBean类型起一个别名。以后就可以直接简单使用别名了 -->
<typeAliases>
<!-- typeAlias:每一个这个标签就是为一个类型起别名 type="com.atguigu.bean.Person":类型全类名 alias="":为这个类型指定一个新的别名。别名=使用的时候不区分大小写 注意:MyBatis已经为我们常见的类型起好了别名。我们自定义别名的时候千万不要占用人家的 -->
<!-- <typeAlias type="com.atguigu.bean.Person" alias="person"/> -->
<!-- 批量起别名 -->
<!--这个包下所有的类都会有一个别名,,默认就是简单类名 -->
<package name="com.atguigu.bean" />
</typeAliases>
<!--类型处理器:MyBatis在底层也是原生的jdbc操作。 MyBatis使用类型处理器来完成对应的java类到数据库类型的映射。 -->
<!--如果类型处理器不够,我们自己编写一个类型处理器注册进来 -->
<!-- 真要自己写:实现 org.apache.ibatis.type.TypeHandler 接口 handler="":指定自定义类型处理器的全类名 -->
<!-- <typeHandlers> <typeHandler handler=""/> </typeHandlers> -->
<!--我们可以编写一个自己的对象工厂,让MyBatis创建对象的时候利用我们的对象工厂创建对象,而不是自己默认的反射机制 -->
<!-- 可以实现ObjectFactory接口。或者继承extends DefaultObjectFactory -->
<!-- <objectFactory type=""></objectFactory> -->
<!-- 插件: 我们可以为MyBatis定制插件:就是一个拦截器。 可以在MyBatis运行的关键位置拦截一道。 Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed) ParameterHandler (getParameterObject, setParameters) ResultSetHandler (handleResultSets, handleOutputParameters) StatementHandler (prepare, parameterize, batch, update, query) 如果要写插件: 1)、我们自定义插件实现Interceptor接口。 2)、在我们自定义插件的类上标注注解。作用:为了告诉MyBatis这个插件拦截哪个对象的哪个方法 -->
<!-- <plugins> <plugin interceptor="插件全类名"></plugin> </plugins> -->
<!-- environments:所有的环境配置在这里 default:默认使用哪个环境 -->
<environments default="development">
<environment id="product">
<!-- 配置事务管理器 type="": JDBC – 这个配置就是直接使用了 JDBC 的提交和回滚设置,它依赖于从数据源得到的连接来管理事务范围。
MANAGED – 这个配置几乎没做什么。它从来不提交或回滚一个连接, 而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。
默认情况下它会关闭连接,然而一些容器并不希望这样, 因此需要将 closeConnection 属性设置为 false 来阻止它默认的关闭行为。
自定义: 1)、自定义事务管理器需要实现TransactionFactory接口 2)、type="":自定义事务管理器的全类名 -->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置数据源 -->
<!-- type="": type=”[UNPOOLED|POOLED|JNDI]”) JNDI:Java Named Directory Interface 自定义: 1)、自定义数据源实现org.apache.ibatis.datasource.DataSourceFactory接口 2)、type="":自定义的全类名 -->
<dataSource type="POOLED">
<property name="driver"value="${driver}" />
<property name="url"value="jdbc:mysql://225.23.45.123:3306/test" />
<property name="username"value="${username}" />
<property name="password"value="${password}" />
</dataSource>
</environment>
<!-- environment:一个标签就是配置一种环境 .id就是当前环境的标识 -->
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver"value="${driver}" />
<property name="url" value="${url}" />
<property name="username"value="${username}" />
<property name="password"value="${password}" />
</dataSource>
</environment>
</environments>
<!--考虑数据库的移植性 -->
<!-- <databaseIdProvider type="DB_VENDOR">
<property name="SQL Server" value="sqlserver" />
<property name="DB2" value="db2" />
<property name="Oracle" value="oracle" />
</databaseIdProvider> -->
<mappers>
<!-- 注册xml文件版的 -->
<!-- url="":配置文件存在于网络的某个位置 -->
<!-- <mapperurl="http://www.atguigu.com/EmpMapper.xml"/> -->
<!-- resource:引用类路径下的资源 -->
<!-- <mapper resource="mybatis/mapper/PersonDao.xml" /> -->
<!-- class: 引入注解版的dao接口;全类名 -->
<!-- <mapper class="com.atguigu.dao.PersonDaoAnnotation" /> -->
<!-- 批量注册:必须满足指定的规则; 1)、文件名必须和接口名一模一样 2)、文件和接口必需放在和接口同一个包下 -->
<package name="com.atguigu.dao"/>
</mappers>
</configuration>
3 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">
<!--public interface PersonDao {} -->
<mapper namespace="com.atguigu.dao.PersonDao">
<!--public void savePerson(Person person);
员工插入:id是自增的。获取刚才插入的员工的id
1)、获取自增主键封装到javaBean
useGeneratedKeys="true":使用自动生成主键
keyProperty:指定哪个属性是用来接受生成的主键的值。
2)、->
<insert id="savePerson" parameterType="Person" useGeneratedKeys="true" keyProperty="id">
<!--order="AFTER":当前这个查询是否在主sql之前运行
keyProperty="id"指定哪个属性封装主键值
resultType="Integer":指定主键的类型 -->
<!-- <selectKey order="BEFORE" keyProperty="id" resultType="Integer">
select crm_seq.nextval from dual
</selectKey> -->
INSERT INTO tbl_person(NAME,age,birth,registerTime,salary)
VALUES(#{name},#{age},#{birth},#{registerTime},#{salary})
</insert>
<!-- public void delPerson(Integer id,String name); -->
<!--传入参数:parameterType指定
1)、简单的参数:
delPerson(Integer id);
多个参数的情况下,第一个参数是param1...paramN
多个参数的情况下,我们推荐使用命名参数。@Param("id")Integer id为参数起名
2)、传入POJO:
两种取值方式
#{对象的属性};进行sql预编译,参数作为占位符传入,安全的
sql:DELETE FROM tbl_person WHERE id=? AND name=?
参数:10(Integer), admin666(String)
更新
${对象的属性};直接将拿到的值进行拼串,不能防止sql注入的安全问题
sql:DELETE FROM tbl_person WHERE id=11 AND name="admin66"
sql注入问题。
以后正常情况都使用#{}进行取值,能防止sql注入
3)、传入map:
4)、口袋POJO:
查询Person对象的时候。需要person.name 需要地址Address.street
MyQueryParam{
Person person;
Address address;
}
SELECT * FROM tbl_person WHERE id=#{person.id} AND street=#{address.street}
-->
<!-- public Person getPersonByIdParaMap(Map<String, Object> map); -->
<select id="getPersonByIdParaMap"resultType="Person"
parameterType="map">
<!--#{key}:从map中取出这个key的值 -->
SELECT * FROM tbl_person WHERE id=#{id}
</select>
<!-- delPersonByCondition -->
<delete id="delPersonByCondition"parameterType="Person">
DELETE FROM tbl_person WHERE id=${id} AND name="${name}"
</delete>
<!--public void delPersonByName(Integer id,String name); -->
<delete id="delPersonByName">
DELETE FROM tbl_person WHERE id=#{id} AND name=#{name}
</delete>
<delete id="delPerson" parameterType="Integer">
DELETE FROM tbl_person WHERE id=#{id}
</delete>
<!-- public void updatePerson(Person person); -->
<update id="updatePerson" parameterType="Person">
UPDATE tbl_person set
name=#{name},age=#{age},birth=#{birth},
registerTime=#{registerTime},salary=#{salary}
WHERE id=#{id}
</update>
<!-- public Person getPersonById(Integer id);
resultType:定义返回值的全类型名 -->
<select id="getPersonById" parameterType="Integer"resultType="Person">
SELECT * FROM tbl_person WHERE id=#{id}
</select>
<!-- public List<Person> getAll();
返回集合的时候,MyBatis能判断出这、是一个集合,
resultType只需要定义集合里面的元素类型即可 -->
<!--resultType="person":指定返回值类型的;
1)、返回值是单个对象。resultType=POJO类型
2)、返回值是个集合。resultType=集合里面元素的类型(POJO类型) -->
<select id="getAll" resultType="person">
SELECT * FROM tbl_person
</select>
<!--public Map<String, Object> getPersonByIdResultMap(Integer id); -->
<!-- 封装map的时候,会自动过滤掉值为null的列-->
<select id="getPersonByIdResultMap"parameterType="Integer"
resultType="map">
SELECT * FROM tbl_person WHERE id=#{id}
</select>
<!--public Map<Integer, Person> getAllMap();
resultType:集合里面元素的类型。元素值数据库记录要封装成的对象-->
<select id="getAllMap" resultType="Person">
SELECT * FROM tbl_person
</select>
</mapper>
4 用注解的方式代替SQL映射文件
可以在PersonDao中的方法上加注解的方式代替SQL映射文件。
【MyBatis配置文件写法】
<mappers>
<!-- 注册,类的全类名 -->
<mapper class="com.atguigu.dao.PersonDaoAnnotation"/>
</mappers>
【PersonDao示例】
public interface PersonDaoAnnotation {
@Insert("INSERT INTO tbl_person(NAME,age,birth,registerTime,salary) "
+ "VALUES(#{name},#{age},#{birth},#{registerTime},#{salary})")
public void savePerson(Person person);
@Delete("DELETE FROM tbl_person WHERE id=#{id}")
public void delPerson(Integer id);
@Update("UPDATE tbl_person set "
+ "name=#{name},age=#{age},birth=#{birth},"
+ "registerTime=#{registerTime},salary=#{salary} "
+ "WHERE id=#{id}")
public void updatePerson(Person person);
@Select("SELECT * FROM tbl_person WHERE id=#{id}")
public Person getPersonById(Integer id);
@Select("SELECT * FROM tbl_person")
public List<Person> getAll();
}
2 动态SQL
1 if/where/trim
1 接口方法
public List<Student> getStudentByConditionIf(Student student);
2 SQL映射文件
<!-- person中哪些属性有值,就按照哪个条件查询,如果多个都有就查询满足所有条件的记录-->
<select id="getStudentByConditionIf" parameterType="Student" resultType="Student">
SELECT * FROM tbl_student
<!-- 一般把查询条件,写在where标签中 -->
<where>
<!--自定义字符串的截取策略。trim里面的字符串最终作为一个整体按照trim规则截取,trim体是空的情况下不会加前缀的 prefix
prefixOverrides:指定的字符串作为整体前缀的时候会被删除掉
suffix: trim里面的字符串整体加一个后缀
suffixOverrides: 指定的字符串作为整体后缀的时候会被删除掉 -->
<trim prefixOverrides="AND | OR" suffixOverrides="AND | OR">
<if test="age != null and age>0">
age > #{age} AND
</if>
<!-- && 相当于&& 或者 and -->
<if test="name != null &&!name.equals("") ">
name LIKE CONCAT('%',#{name},'%') AND
</if>
<if test="id != null">
id=#{id} AND
</if>
</trim>
</where>
</select>
2 choose/when/otherwise
1接口方法
public List<Student> getStudentByConditionChoose(Student student);
2 SQL映射
<!-- 需求:如果带了id用id查,没带id看是否带了name,带了name用name查。 最终只会有一个条件会进入。 -->
<!-- public List<Student> getStudentByConditionChoose(Student studsent); -->
<select id="getStudentByConditionChoose" parameterType="Student" resultType="Student">
SELECT * FROM tbl_student
<where>
<choose>
<when test="id != null">
id=#{id}
</when>
<when test="name != null and !name.equals("")">
name=#{name}
</when>
<otherwise>
id=1;
</otherwise>
</choose>
</where>
</select>
3 foreach
相当于sql语句中的IN查询
1 接口方法
public List<Student> getStudentByForeach(@Param("ids")List<Integer> ids);
2 SQL映射
<select id="getStudentByForeach" parameterType="list" resultType="Student">
SELECT * FROM tbl_student
<where>
<!-- collection:指定要遍历的集合。
item:遍历出的元素赋值给item
open:以什么开始
close:以什么结束
separator:指定遍历出的元素 以什么分隔-->
<foreach collection="ids" item="id" open="id in(" close=")" separator=",">
#{id}
</foreach>
</where>
</select>
4 set
用于更新
set动态设置set关键字
Set可以自动删除多余的英文逗号
1 接口方法
public void updateStudent(Student student);
2 SQL映射
<update id="updateStudent" parameterType="Student">
UPDATE tbl_student
<!-- 动态设置set关键字 去除多余的逗号 -->
<set>
<if test="name !=null">
NAME=#{name},
</if>
<if test="score>0">
score=#{score},
</if>
<if test="birth !=null">
birth=#{birth},
</if>
<if test="age>0">
age=#{age}
</if>
WHERE id=#{id}
</set>
</update>
3 关联查询
1 一对一
1 连接查询
接口方法:
public Lock getLockById(Integer id);
SQL映射:
<resultMap type="Lock" id="lockResult">
<id column="id" property="id"/>
<result column="lockName" property="lockName"/>
<association property="key" javaType="Key">
<id column="id" property="id"/>
<result column="keyName" property="keyName"/>
</association>
</resultMap>
<select id="getLockById" parameterType="integer" resultMap="lockResult">
SELECT * FROM tbl_lock,tbl_key WHERE tbl_lock.id=#{id} and tbl_lock.key_id=tbl_key.id
</select>
2 分段查询
接口方法:
public Lock getLockByStep(Integer id);
public Key getKeyById(Integer id);
SQL映射:
<resultMap type="Lock" id="my_lock">
<id column="id" property="id"/>
<result column="lockName" property="lockName"/>
<!-- select:声明这个property是通过select指定的sql查询得到的-->
<association property="key" column="key_id" select="dao.KeyDao.getKeyById"></association>
</resultMap>
<select id="getLockByStep" parameterType="integer" resultMap="my_lock">
SELECT * FROM tbl_lock WHERE id=#{id};
</select>
3 延时加载
Lock中的key默认每次查询Lock时,key一定根据select指定的sql语句查询,并封装好。但是有时候我们不需要key对象,这样会浪费资源,所以希望在我们用到key时再去查。
延时加载步骤:
1 导入cglib
cglib-2.2.2.jar
asm-3.3.1.jar
2 在myBatis全局配置文件中开启延迟加载策略
<settings>
<setting name="lazyLoadingEnabled"value="true"/>
<!-- 属性按需加载 ,这个功能使必须cglib支持的。-->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
2 一对多
1 员工有一个部门属性
接口方法:
public Employee getEmpById(Integer id);
public Department getDeptById(Integer id);
SQL映射:
<resultMap type="Employee" id="my_emp">
<id column="id" property="id"/>
<result column="name" property="name"/>
<association property="dept"
select="dao.DeptDao.getDeptById"
column="deptId"/>
</resultMap>
<select id="getEmpById" parameterType="integer" resultMap="my_emp">
SELECT * FROM tbl_emp WHERE id=#{id}
</select>
2 部门都多个员工
接口方法:
public Department getDeptById(Integer id);
public Employee getEmpById(Integer id);
SQL映射:
<resultMap type="Department" id="my_dept">
<id column="id" property="id"/>
<result column="deptName" property="deptName"/>
<result column="locAdd" property="locAdd"/>
<!-- emps是一个集合 -->
<collection property="emps"
select="dao.EmpDao.getEmpByDeptId"
column="id">
</collection>
</resultMap>
<select id="getDeptById" parameterType="integer" resultMap="my_dept">
SELECT * FROM tbl_dept WHERE id=#{id}
</select>
4 缓存机制
1 一级默认缓存机制
1 默认
默认情况下,mybatis是启用一级缓存的,是SQLSession级别的,即同一个SQLSession接口对象调用相同的select语句。
默认情况下,select使用缓存,增删改不使用缓存。
当SQLSession flush(刷新)close(关闭)后,该session中的所以Cache将全部清空。
2 一级缓存失效的情况
在底层,缓存保存时,使用方法的签名+hashCode+查询sql的id+查询的参数
1 同一个SQLSession,查询条件不同时。因为在底层,所以还会去数据库查询。
2 SQLSession不同时。即使查询条件一样,也会再次发sql查询。
3 同一个SQLSession,查询条件一样。两次查询期间,执行任何一次增删改操作,也会重新查询。
4 同一个SQLSession,手动的清除缓存:session.clearCache()。
2 二级缓存机制
1 二级机制原理
第一次查出的数据会被放在一级缓存中。只有关闭了SQLSession后,一级缓存中的所有数据会同步到二级缓存中。
查询时,会先去二级缓存中看,如果二级缓存中没有,再去一级缓存中,一级缓存中没有,就查询数据库了。
2 开启二级缓存
1 开启全局二级缓存。在mybatis全局配置文件中配置。
<settings>
<!-- 开启全局缓存 -->
<setting name="cacheEnabled" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
2 SQL映射中配置缓存策略
<!-- 开启二级缓存
eviction:回收策略。
可用的收回策略有:默认的是 LRU。
LRU – 最近最少使用的:移除最长时间不被使用的对象。
FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
flushInterval(刷新间隔)可以被设置为任意的正整数,而且它们代表一个合理的毫秒形式的时间段。默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新。
size(引用数目)可以被设置为任意正整数,要记住你缓存的对象数目和你运行环境的可用内存资源数目。默认值是 1024。
readOnly(只读)属性可以被设置为 true 或 false。
false可读写的缓存:会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是 false。
true:如果从缓存中获取数据,数据的引用会直接交出去。(外界一但一修改缓存里面的东西也就改了),不安全,速度快。
type:如果是默认的type不用写,自定义的必须写自定义cache全类名,实现MyBatis提供的Cache接口
-->
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"></cache>
3 bean必须实现序列化接口
public class User implements Serializable
3 整合第三方缓存ehcache
ehcache是一个强大的缓存框架,我们可以使用ehcache来替代mybatis的二级缓存。
1 导包
ehcache-core-2.6.8.jar //ehcache官方核心包
mybatis-ehcache-1.0.3.jar //ehcache与mybatis的整合包
slf4j-api-1.6.1.jar、slf4j-log4j12-1.6.2.jar // ehcache要用的日志包
2 编写配置文件
1 配置文件名字必须是ehcache.xml。放在类路径下
2 内容:
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
<!-- 磁盘保存路径 -->
<diskStore path="D:\44\ehcache" />
<defaultCache maxElementsInMemory="1000" maxElementsOnDisk="10000000" eternal="false" overflowToDisk="false" timeToIdleSeconds="120" timeToLiveSeconds="120" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU">
</defaultCache>
</ehcache>
<!--
属性说明:
diskStore:指定数据在磁盘中的存储位置。
defaultCache:当借助CacheManager.add("demoCache")创建Cache时,EhCache便会采用<defalutCache/>指定的的管理策略
以下属性是必须的:
maxElementsInMemory - 在内存中缓存的element的最大数目
maxElementsOnDisk - 在磁盘上缓存的element的最大数目,若是0表示无穷大
eternal - 设定缓存的elements是否永远不过期。如果为true,则缓存的数据始终有效,如果为false那么还要根据timeToIdleSeconds,timeToLiveSeconds判断
overflowToDisk - 设定当内存缓存溢出的时候是否将过期的element缓存到磁盘上
以下属性是可选的:
timeToIdleSeconds - 当缓存在EhCache中的数据前后两次访问的时间超过timeToIdleSeconds的属性取值时,这些数据便会删除,默认值是0,也就是可闲置时间无穷大
timeToLiveSeconds - 缓存element的有效生命期,默认是0.,也就是element存活时间无穷大
diskSpoolBufferSizeMB 这个参数设置DiskStore(磁盘缓存)的缓存区大小.默认是30MB.每个Cache都应该有自己的一个缓冲区.
diskPersistent - 在VM重启的时候是否启用磁盘保存EhCache中的数据,默认是false。
diskExpiryThreadIntervalSeconds - 磁盘缓存的清理线程运行间隔,默认是120秒。每个120s,相应的线程会进行一次EhCache中数据的清理工作
memoryStoreEvictionPolicy - 当内存缓存达到最大,有新的element加入的时候, 移除缓存中element的策略。默认是LRU(最近最少使用),可选的有LFU(最不常使用)和FIFO(先进先出)
-->
3 SQL映射中配置使用ehcache
<cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>
4 bean不再需要实现序列化接口
4 和缓存有关的设置
1 <setting name="cacheEnabled" value="true"/>
作用:操作二级缓存,value必须是true。一级缓存本来就是默认开启的。
2 每一个sql标签都有一个flushCache 属性。增删改操作默认是true,即刷新缓存。一级二级缓存都会被清除。查询操作默认是false,如果设置为true,会禁用二级缓存,不会禁用一级。
3 select标签的useCache属性。true时,使用缓存。False是只能禁用二级缓存,一级依然开启。因此只有flushCache=”true”可以禁用一级缓存,其他设置不能改变一级缓存。
5 MyBatis&Spring&SpringMVC
1 导包
1 Spring环境
1 Spring核心容器+日志
commons-logging-1.1.3.jar
spring-beans-4.0.0.RELEASE.jar
spring-context-4.0.0.RELEASE.jar
spring-core-4.0.0.RELEASE.jar
spring-expression-4.0.0.RELEASE.jar
2 aop模块
com.springsource.net.sf.cglib-2.2.0.jar
com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
spring-aop-4.0.0.RELEASE.jar
spring-aspects-4.0.0.RELEASE.jar
3 数据库模块(只用到声明式事务功能,操作数据库用MyBatis)
spring-jdbc-4.0.0.RELEASE.jar
spring-orm-4.0.0.RELEASE.jar
spring-tx-4.0.0.RELEASE.jar
2 SpringMVC
1 基础模块
spring-web-4.0.0.RELEASE.jar
spring-webmvc-4.0.0.RELEASE.jar
2 文件上传
commons-fileupload-1.2.1.jar
commons-io-2.0.jar
3 jstl标签库
jstl.jar
Standard.jar
4 数据校验(JSR303)
核心校验:
hibernate-validator-5.0.0.CR2.jar
hibernate-validator-annotation-processor-5.0.0.CR2.jar
依赖包:
classmate-0.8.0.jar
jboss-logging-3.1.1.GA.jar
validation-api-1.1.0.CR1.jar
5 Ajax
jackson-annotations-2.1.5.jar
jackson-core-2.1.5.jar
jackson-databind-2.1.5.jar
6 验证码
kaptcha-2.3.2.jar
3 MyBatis
1 MyBatis核心jar包
mybatis-3.2.8.jar
日志:log4j.jar
2 延迟加载功能依赖cglib
asm-3.3.1.jar
cglib-2.2.2.jar
3 MyBatis使用ehcache做二级缓存
ehcache-core-2.6.8.jar
mybatis-ehcache-1.0.3.jar
slf4j-api-1.6.1.jar
slf4j-log4j12-1.6.2.jar
4 数据库驱动、连接池
mysql-connector-java-5.1.37-bin.jar
c3p0-0.9.1.2.jar
2 配置文件
目录结构:
1 Spring配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<context:component-scan base-package="com.atguigu">
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Controller" />
<context:exclude-filter type="annotation"
expression="org.springframework.web.bind.annotation.ControllerAdvice" />
</context:component-scan>
<!-- 因为外部数据源配置文件 -->
<context:property-placeholder location="classpath:dbConfig.properties" />
<!-- 配置数据源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="jdbcUrl" value="${jdbc.url}"></property>
<property name="driverClass" value="${jdbc.driver}"></property>
</bean>
<!-- 配置事务控制 MyBatis依靠SqlSessionFactory来创建会话 Spring应该管理SqlSessionFactory,并让其自动创建会话 -->
<!-- mybatis和Spring的整合, 需要一个中间包:mybatis-spring-1.2.2.jar 自动创建映射,自动加载到ioc容器中 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 指定mybatis的全局配置文件 -->
<property name="configLocation" value="classpath:mybatis/mybatis-config.xml"></property>
<!-- 指定数据源 -->
<property name="dataSource" ref="dataSource"></property>
<property name="mapperLocations" value="classpath:mybatis/mapper/*.xml"></property>
<property name="typeAliasesPackage" value="com.atguigu.bean"></property>
</bean>
<!-- 能自动的扫描所有的mapper的实现 -->
<bean id="configure" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 这个包下的所有mapper实现自动扫描并且自动加载到ioc容器 -->
<property name="basePackage" value="com.atguigu.dao"></property>
</bean>
<!-- 配置声明式事务 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 开启基于注解的声明式事务 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
2 SpringMVC配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<context:component-scan base-package="com.atguigu"
use-default-filters="false">
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Controller" />
<context:include-filter type="annotation"
expression="org.springframework.web.bind.annotation.ControllerAdvice" />
</context:component-scan>
<!-- 配置视图解析器 -->
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<!-- 标配 -->
<!-- 动态资源 -->
<mvc:annotation-driven></mvc:annotation-driven>
<!-- 静态资源 -->
<mvc:default-servlet-handler />
</beans>
3 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">
<!-- dtd文件是xml文档的约束文件。这个约束文件就是规定这个xml能写什么标签的。 有个这个约束就知道能写什么? 只要这个约束文件的位置被正确指向,就会有提示。
给eclipse配置http://mybatis.org/dtd/mybatis-3-config.dtd约束文件的所在位置 在jar包中的org.apache.ibatis.builder.xml下有,直接拿出来 -->
<configuration>
<properties resource="dbConfig.properties">
</properties>
<settings>
<!-- 开启全局缓存 -->
<setting name="cacheEnabled" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
4 log4j配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
<param name="Encoding" value="UTF-8" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%-5p %d{MM-dd HH:mm:ss,SSS} %m (%F:%L) \n" />
</layout>
</appender>
<logger name="java.sql">
<level value="debug" />
</logger>
<logger name="org.apache.ibatis">
<level value="debug" />
</logger>
<root>
<level value="debug" />
<appender-ref ref="STDOUT" />
</root>
</log4j:configuration>
5 数据源dbconfig.properties配置文件
jdbc.username=root
jdbc.password=123456
jdbc.url=jdbc:mysql:///person
jdbc.driver=com.mysql.jdbc.Driver
6 ehcache配置文件
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
<!-- 磁盘保存路径 -->
<diskStore path="D:\44\ehcache" />
<defaultCache
maxElementsInMemory="1000"
maxElementsOnDisk="10000000"
eternal="false"
overflowToDisk="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
</defaultCache>
</ehcache>
<!--
属性说明:
diskStore:指定数据在磁盘中的存储位置。
defaultCache:当借助CacheManager.add("demoCache")创建Cache时,EhCache便会采用<defalutCache/>指定的的管理策略
以下属性是必须的:
maxElementsInMemory - 在内存中缓存的element的最大数目
maxElementsOnDisk - 在磁盘上缓存的element的最大数目,若是0表示无穷大
eternal - 设定缓存的elements是否永远不过期。如果为true,则缓存的数据始终有效,如果为false那么还要根据timeToIdleSeconds,timeToLiveSeconds判断
overflowToDisk - 设定当内存缓存溢出的时候是否将过期的element缓存到磁盘上
以下属性是可选的:
timeToIdleSeconds - 当缓存在EhCache中的数据前后两次访问的时间超过timeToIdleSeconds的属性取值时,这些数据便会删除,默认值是0,也就是可闲置时间无穷大
timeToLiveSeconds - 缓存element的有效生命期,默认是0.,也就是element存活时间无穷大
diskSpoolBufferSizeMB 这个参数设置DiskStore(磁盘缓存)的缓存区大小.默认是30MB.每个Cache都应该有自己的一个缓冲区.
diskPersistent - 在VM重启的时候是否启用磁盘保存EhCache中的数据,默认是false。
diskExpiryThreadIntervalSeconds - 磁盘缓存的清理线程运行间隔,默认是120秒。每个120s,相应的线程会进行一次EhCache中数据的清理工作
memoryStoreEvictionPolicy - 当内存缓存达到最大,有新的element加入的时候, 移除缓存中element的策略。默认是LRU(最近最少使用),可选的有LFU(最不常使用)和FIFO(先进先出)
-->
3 测试
1 bean层user.java
public class User{
private Integer id;
private String name;
private Integer age;
2 dao层UserDao.java
public interface UserDao {
public User getUserById(Integer id);
public void addUser(User user);
}
3 dao层的SQL映射文件userMapper.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">
<!-- 这个文件的作用就是相当于对PersonDao的每一个方法的实现 namespace:必须是接口的全类名,这样MyBatis才知道这个配置文件是相当于对哪个接口的实现 -->
<mapper namespace="com.atguigu.dao.UserDao">
<select id="getUserById" parameterType="Integer" resultType="User" >
SELECT * FROM tbl_user WHERE id = #{id}
</select>
<insert id="addUser" parameterType="User">
INSERT INTO tbl_user(NAME,age) VALUES(#{name},#{age})
</insert>
</mapper>
4 service层
@Service
public class UserService {
@Autowired
private UserDao userDao;
public User getUserById(Integer id){
return userDao.getUserById(id);
}
}
5 controller
@Controller
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("/getUser")
public String getUser(@RequestParam("id") Integer id,Map<String,Object> map){
User user = userService.getUserById(id);
map.put("user", user);
return "success";
}
}
6 jsp页面
index.jsp
<a href="getUser?id=1">查询一号用户</a>
Success.jsp
<h1>${user }</h1>
6 MyBatis****
数据库建表,根据数据表,自动生成javaBean、接口、xml映射文件
MyBatis Generator(MBG):MyBatis代码生成器
使用步骤:
1 导包
mybatis-generator-core-1.3.2.jar
mysql-connector-java-5.1.37-bin.jar//数据库驱动
2 配置文件
<?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>
<!--<classPathEntry location="/Program Files/IBM/SQLLIB/java/db2java.zip" /> -->
<!-- context:环境
id:唯一标示
targetRuntime:运行时环境 ,可选:MyBatis3、MyBatis3Simple-->
<context id="DB2Tables" targetRuntime="MyBatis3Simple">
<!-- jdbcConnection:jdbc连接 -->
<jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://localhost:3306/person"
userId="root"
password="123456">
</jdbcConnection>
<javaTypeResolver >
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>
<!-- 生成Javabean的配置
targetPackage:生成的JavaBean放在哪个包下
targetProject:放在哪个工程下-->
<javaModelGenerator targetPackage="com.atguigu.bean" targetProject=".\src">
<property name="enableSubPackages" value="true" />
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!-- sqlMapGenerator:SQL映射文件配置 -->
<sqlMapGenerator targetPackage="mybatis.mapper" targetProject=".\conf">
<property name="enableSubPackages" value="true" />
</sqlMapGenerator>
<!-- Java客户端生成:Dao接口 -->
<javaClientGenerator type="XMLMAPPER" targetPackage="com.atguigu.dao" targetProject=".\src">
<property name="enableSubPackages" value="true" />
</javaClientGenerator>
<table tableName="tbl_dept" domainObjectName="Department"></table>
<table tableName="tbl_user" domainObjectName="User"></table>
</context>
</generatorConfiguration>
3 测试
public class MBGTest {
public static void main(String[] args) throws Exception {
List<String> warnings = new ArrayList<String>();
boolean overwrite = true;
//获取配置文件,配置文件在当前工程下
File configFile = new File("mbg.xml");
ConfigurationParser cp = new ConfigurationParser(warnings);
Configuration config = cp.parseConfiguration(configFile);
DefaultShellCallback callback = new DefaultShellCallback(overwrite);
MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
myBatisGenerator.generate(null);
System.out.println("运行完成");
}
}
7 MyBatis插件
1 分页插件
1 导包
pagehelper-5.0.0.jar
jsqlparser-0.9.5.jar //sql解析工具
2 配置文件
在mybatis-config.xml中,配置插件。
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
</plugin>
</plugins>
3 方法调用
public static void main(String[] args) throws Exception {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory builder = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession openSession = builder.openSession();
BookMapper bookMapper = openSession.getMapper(BookMapper.class);
//配置分页
/*
* 1、导包
* 2、配置插件
* 3、调用
*/
Page<Object> page = PageHelper.startPage(7, 5);
List<Book> list = bookMapper.selectAll();
System.out.println("当前页面:"+page.getPageNum());
System.out.println("总页数:"+page.getPages());
System.out.println("总记录数:"+page.getTotal());
System.out.println(list.size());
for (Book book : list) {
System.out.println(book);
}
openSession.close();
}