功能篇1 日志、事务、延迟加载

准备:

  1. 此处只讨论Category和Proudct关系,因此数据库初始化只需要对这两个表进行初始化就行了。

    1. drop database how2java;
    2. create database how2java;
    3. use how2java;
    4. -- 分类表
    5. CREATE TABLE category_ (
    6. id int(11) NOT NULL AUTO_INCREMENT,
    7. name varchar(32) DEFAULT NULL,
    8. PRIMARY KEY (id)
    9. ) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
    10. -- 产品表
    11. create table product_(
    12. id int NOT NULL AUTO_INCREMENT,
    13. name varchar(30) DEFAULT NULL,
    14. price float DEFAULT 0,
    15. cid int ,
    16. PRIMARY KEY (id)
    17. )AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
    18. -- 插入数据
    19. delete from category_;
    20. INSERT INTO category_ VALUES (1,'衣服');
    21. INSERT INTO category_ VALUES (2,'玩具');
    22. delete from product_;
    23. INSERT INTO product_ VALUES (1,'帽子', 30, 1);
    24. INSERT INTO product_ VALUES (2,'鞋子', 20, 1);
    25. INSERT INTO product_ VALUES (3,'袜子', 10, 1);
    26. INSERT INTO product_ VALUES (4,'变形金刚', 50, 2);
    27. INSERT INTO product_ VALUES (5,'赛车', 70, 2);
    28. INSERT INTO product_ VALUES (6,'芭比娃娃', 100.5, 2);
  2. 通过注解的方式实现了Category与Product的1对多关系。

    1. CategoryMapper
  1. // 根据id获取单个Category
  2. @Select("select * from category_ where id= #{id} ")
  3. public Category get(int id);
  4. // 查询所有种类的id、name、包含的商品
  5. @Select(" select * from category_")
  6. @Results({ @Result(property = "id", column = "id"), @Result(property = "name", column = "name"),
  7. // many属性表示一对多的关系,是另一个映射器里的查询结果
  8. // 把Category的id传入到查询根据cid查询Product的方法里,获得一个列表
  9. @Result(property = "products", javaType = List.class, column = "id", many = @Many(select = "com.huang.mapper.ProductMapper.listBycid")) })
  10. public List<Category> list();
  1. ProductMapper
  1. // 根据商品种类id查询,简单信息
  2. @Select("select * from product_ where cid=#{cid}")
  3. public List<Product> listBycid(int cid);
  4. // 查询所有商品的基本信息、所属种类
  5. @Select("select * from product_")
  6. @Results({ @Result(property = "id", column = "id"), @Result(property = "name", column = "name"),
  7. @Result(property = "category", column = "cid", one = @One(select = "com.huang.mapper.CategoryMapper.get")) })
  8. public List<Product> list();

1 日志

1.1 导入日志包log4j

  1. 在lib文件夹下的 log4j.jar包build path。
  2. 在src目录下新建输出配置文件log4j.properties。用properties输出,输出等级为debug在控制台stdout输出。Eclipse默认的.properties配置文件的编码方式不合适,不能打中文,需要手动更改为UTF-8.
  1. # 全局配置
  2. log4j.rootLogger=debug, stdout
  3. # 某个包下的输出级别
  4. log4j.logger.com.huang=debug
  5. # 输出格式配置
  6. log4j.appender.stdout=org.apache.log4j.ConsoleAppender
  7. log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
  8. log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n
  1. 更多输出配置项可以看以前的文章日志工具log4j

    1.2 测试配置

    Demo文件中查询id为1和2的Category
  1. CategoryMapper mapper = session.getMapper(CategoryMapper.class);
  2. System.out.println("查询第1个:");
  3. Category c1=mapper.get(1);
  4. System.out.println(c1);
  5. System.out.println("查询第2个:");
  6. Category c2=mapper.get(2);
  7. System.out.println(c2);

1.3 日志有什么用

  1. MyBatis底层会自动输出日志信息,开启Debug信息可以看到与数据库的各种连接信息
  2. 图片.png

    2 事务

    2.1 事务的特性

  3. ACID特性

    原子性(Atomicity) 原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。

一致性(Consistency)

事务前后数据的完整性必须保持一致。

隔离性(Isolation)

事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。

持久性(Durability)

持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响

  1. 事务的隔离级别。数据库

    2.2 开启事务的功能

  2. 有问题的示范

    1. 先创建一个xml映射文件用来对数据库进行增删改操作。
  1. <mapper namespace="com.huang.mapper.CategoryMapper">
  2. <insert id="insertCategory" parameterType="Category" >
  3. insert into category_ ( name ) values (#{name})
  4. </insert>
  5. <delete id="deleteCategory" parameterType="Category" >
  6. delete from category_ where id= #{id}
  7. </delete>
  8. <update id="updateCategory" parameterType="Category" >
  9. update category_ set name=#{name} where id=#{id}
  10. </update>
  11. </mapper>
  1. 在mybatis主配置文件中声明mapper。
  1. <mapper resource="com/huang/mapper/CategoryMapper.xml" />
  1. 注意两个有关命名的点
    1. 主配置文件中的typealias标签,某个映射文件中映射项参数或返回结果是模型类,例如参数类型为com.huang.model.Category,在主配置文件中使用了这个标签,就只需要写Category。很方便。
  1. <typeAliases>
  2. <package name="com.huang.model" />
  3. </typeAliases>
  1. 1. 映射文件中的namespace属性,一个项目里多个映射文件可能会有写映射项重复,重复的话就需要namespace+映射项名子来指定。namesapce通常就是当前所在的xml文件名。
  1. <mapper namespace="com.huang.mapper.CategoryMapper">
  1. 写一个Demo测试,一个事务中两个插入操作,第一个插入操作正常,第二个插入操作名字很长,肯定会失败。
  1. System.out.println("新增加id为3");
  2. session.insert("insertCategory","id为3的category");
  3. System.out.println("新增加id为4:");
  4. session.insert("insertCategory","id为4的category,hahahahhahhahahhahahahahhahahhahahahhahahhahahah");
  1. 正确的操作
    1. 发现上述没有效果,没有体现事务的原子性。图片.png
    2. 原因是这个Category表的引擎不对,应该使用InnoDB。
  1. show table status from how2java;
  1. 重新初始化数据库信息,并且使用InnoDB引擎后可以看到原子性。
  1. alter table category_ ENGINE = innodb;

3 延迟加载

3.1 什么是延迟加载

  1. 基于前面注解的Category与Product一对多的方式进行查询。只输出Category的名字。
  1. CategoryMapper mapper=session.getMapper(CategoryMapper.class);
  2. List<Category> cs = mapper.list();
  3. for (Category c : cs) {
  4. System.out.println(c.getName());
  5. // List<Product> ps = c.getProducts();
  6. // for (Product p : ps) {
  7. // System.out.println("\t"+p.getName());
  8. // }
  9. }
  1. 修改日志配置输出界别trace,显示更加具体的信息。
  1. log4j.logger.com.huang=trace
  1. 发现即使没有输出相关的Product信息,也会进行Product查询。图片.png
  2. 延迟加载希望不输出相关Product信息就不要查询了减少对数据库的操作。

    3.2 开启延迟加载

  3. 修改MyBatis主配置。必须在configuration标签下第一个配置这个标记。

  1. <settings>
  2. <!-- 打开延迟加载的开关 -->
  3. <setting name="lazyLoadingEnabled" value="true" />
  4. <!-- 将积极加载改为消息加载即按需加载 -->
  5. <setting name="aggressiveLazyLoading" value="false" />
  6. </settings>
  1. 可以发现不必要的查询就没了。图片.png
  2. 开启输出关联的信息是才会有多出来的查询图片.png

    4 总结

    4.1 日志功能

  3. 注意日志配置文件的编码。

  4. 日志配置文件的全局配置与局部包配置。
  5. 通过日志文件观察与数据库交互的情况。

    4.2 事务

  6. 事务的ACID特性

  7. 数据表引擎应该为InnoDB

    4.3 延迟加载

  8. 延迟加载可以减少不必要的查询。

  9. 延迟加载在setting节点里配置,并且setting结点应该为configuration结点下的第一个结点