目次
3.2 MyBatisコメントの追加、削除、変更、確認[キー]
3.3アノテーションを使用して複雑なマッピング開発を実現する
3.7.1セカンダリキャッシュのサポートを有効にするようにSqlMapConfig.xmlファイルを構成します
3.7.2注釈を使用して、マッパーインターフェイスで第2レベルのキャッシュを構成します
Mybatisの読み込み戦略とアノテーションの開発
MyBatisの読み込み戦略
1.1遅延読み込みとは何ですか?
問題
これまでの調査を通じて、Mybatisでの1対1、1対多、および多対多の関係の構成と実装を習得し、関連するオブジェクトのクエリを実現できます。
実際の開発プロセスでは、ユーザーの注文情報をロードするときに、必ずしもユーザーの注文情報をロードする必要はありません。これは、遅延読み込みと呼ばれるものです。
栗をあげる
遅延読み込み
必要なときにのみデータをロードし、不要なときにデータをロードしません。遅延読み込みは、遅延読み込みとも呼ばれます。
1.2実現
1.2.1ローカル遅延読み込み
アソシエーションタグとコレクションタグにはfetchType属性があり、その値を変更することで、ローカルの読み込み戦略を変更できます。
<!--一对多嵌套查询:查询所有的用户,同时还要查询出每个用户所关联的订单信息-->
<resultMap id="userOrderMap" type="com.lagou.domain.User">
<id property="id" column="id"/>
<result property="username" column="username"></result>
<result property="birthday" column="birthday"></result>
<result property="sex" column="sex"></result>
<result property="address" column="address"></result>
<!--fetchType="lazy" : 延迟加载策略
fetchType="eager": 立即加载策略
-->
<collection property="ordersList" ofType="com.lagou.domain.Orders" column="id"
select="com.lagou.mapper.OrderMapper.findByUid" fetchType="lazy"></collection>
</resultMap>
<select id="findAllWithOrder2" resultMap="userOrderMap">
SELECT * FROM USER
</select>
1.2.2遅延ロードをトリガーする方法の設定
遅延読み込み戦略を構成した後、関連付けられたオブジェクトのメソッドを呼び出さなかった場合でも、現在のオブジェクトのequals、clone、hashCode、およびtoStringメソッドを呼び出すと、関連付けられたオブジェクトのクエリがトリガーされることがわかりました。
構成ファイルのlazyLoadTriggerMethods構成項目を使用して、上記の4つのメソッドをオーバーライドできます。
<!-- 配置在configuration下面 -->
<settings>
<!--toString()相关方法会延迟加载-->
<setting name="lazyLoadTriggerMethods" value="toString()"/>
</settings>
1.2.3グローバル遅延読み込み
Mybatisのコア構成ファイルでは、設定タグを使用してグローバルロード戦略を変更できます。
<!-- 配置在configuration下面 -->
<settings>
<!--开启全局延迟加载功能-->
<setting name="lazyLoadingEnabled" value="true"/>
<!--所有方法都会延迟加载-->
<setting name="lazyLoadTriggerMethods" value="toString()"/>
</settings>
ローカルローディング戦略は、グローバルローディング戦略よりも優先度が高いことに注意してください。
<resultMap id="orderMap2" type="com.lagou.domain.Orders">
<id property="id" column="id"/>
<result property="ordertime" column="ordertime"/>
<result property="total" column="total"/>
<result property="uid" column="uid"/>
<!--问题:1.怎么去执行第二条sql , 2.如何执行第二条sql的时候,把uid作为参数进行传递
下面的select用来调用第二条sql
column 是要传递的参数, -->
<association property="user" javaType="com.lagou.domain.User"
select="com.lagou.mapper.UserMapper.findById" column="uid" fetchType="eager"/>
</resultMap>
<!--一对一嵌套查询-->
<select id="findAllWithUser2" resultMap="orderMap2">
SELECT * FROM orders
</select>
2つのMyBatisキャッシュ
2.1なぜキャッシュを使用するのですか?
ユーザーが特定の固定データを頻繁にクエリすると、データベースから初めてクエリが実行され、キャッシュに保存されます。ユーザーがデータを再度クエリする場合、データベースを介してクエリを実行する必要はありませんが、キャッシュ内でクエリを実行する必要があります。
ネットワーク接続とデータベースクエリによって引き起こされる損失を減らし、それによってクエリの効率を改善し、高い同時アクセスによって引き起こされるシステムパフォーマンスの問題を減らします。
一文の要約:頻繁に変更されないデータをクエリし、キャッシュを使用してクエリの効率を向上させることがよくあります。
ほとんどの永続化フレームワークと同様に、Mybatisはキャッシュ戦略も提供します。これにより、データベースクエリの数を減らし、パフォーマンスを向上させることができます。Mybatisのキャッシュは、第1レベルのキャッシュと第2レベルのキャッシュに分けられます。
2.2レベル1キャッシュ
2.2.1はじめに
第1レベルのキャッシュはSqlSessionレベルのキャッシュであり、デフォルトでオンになっています(インタビューで尋ねられる場合があります)。
したがって、パラメーターがSQLとまったく同じである場合、同じSqlSessionオブジェクトを使用してMapperメソッドを呼び出し、多くの場合SQLのみを実行します。一度、SelSessionが使用されるため、最初のクエリの後、MyBatisはそれをキャッシュに入れます。後でクエリを実行するときに、更新する必要があるというステートメントがなく、キャッシュがタイムアウトしない場合、SqlSessionは現在のキャッシュデータをフェッチします。 SQLをデータベースに送信する代わりに。
2.2.2検証
ログ構成ファイルを使用してSQLの実行を表示します
### direct log messages to stdout ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
### direct messages to file mylog.log ###
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=c:/mylog.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
### set log levels - for more verbose logging change 'info' to 'debug' ###
log4j.rootLogger=debug, stdout
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.7</version>
</dependency>
/*
验证mybatis中的一级缓存
*/
@Test
public void testOneCache() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 根据id查询用户信息
// 第一次查询,查询的数据库
User user1 = userMapper.findById(1);
System.out.println(user1);
// clearCache: 手动清空缓存
//sqlSession.clearCache();
// 第二次查询,查询的是一级缓存
User user2 = userMapper.findById(1);
System.out.println(user2);
sqlSession.close();
}
上記のコードで2回クエリを実行しましたが、最終的に実行したデータベース操作は1つだけでした。これは、Mybatisから提供された最初のレベルのキャッシュが機能していることです。第1レベルのキャッシュが存在するため、ID 1のレコードを2回クエリするときに、データベースからデータをクエリするためのSQLステートメントは発行されませんでしたが、第1レベルのキャッシュがクエリされました。
2.2.3分析
第1レベルのキャッシュは、SqlSessionのスコープ内のキャッシュです。SqlSessionのC(増加)U(更新)D(削除)操作を実行するか、clearCache()、commit()、close()メソッドを呼び出すと、キャッシュがクリアされます。
2.2.4クリア
<!--根据id查询用户
每次查询时,都会自动清除缓存
-->
<select id="findById" resultType="com.lagou.domain.User" parameterType="int" flushCache="true">
SELECT * FROM user WHERE id = #{id}
</select>
2.3セカンダリキャッシュ
2.3.1はじめに
2番目のレベルのキャッシュはnamspaceレベル(sqlSession全体)のキャッシュであり、デフォルトでは有効になっていません
。2番目のレベルのキャッシュのアクティブ化には構成が必要です。2番目のレベルのキャッシュを実装する場合、MyBatisによって返されるPOJOはシリアル化可能である必要があります。
Serializableインターフェースを実装する必要があります。設定方法は非常に簡単です。セカンダリキャッシュを有効にするには、マッピングXMLファイルで<cache />を設定するだけで済みます。
2.3.2検証
a)コア構成ファイルを構成します
<!-- 配置在configuration下面 -->
<settings>
<!--
因为cacheEnabled的取值默认就为true,所以这一步可以省略不配置。
为true代表开启缓存;为false代表不开启缓存。
-->
<setting name="cacheEnabled" value="true"/>
</settings>
b)UserMapper.xmlマッピングを構成します
<mapper namespace="com.lagou.mapper.UserMapper">
<!--当前映射开启二级缓存-->
<cache></cache>
<!--根据id查询用户
useCache="true" 代表当前这个statement是使用二级缓存
-->
<select id="findById" resultType="com.lagou.domain.User" parameterType="int" useCache="true">
SELECT * FROM user WHERE id = #{id}
</select>
</mapper>
c)ユーザーエンティティをシリアル化する
// 序列化
public class User implements Serializable {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
// 表示多方关系:集合 : 代表了当前用户所具有的订单列表 collection
private List<Orders> ordersList;
// 表示多方关系:集合 : 代表了当前用户所具有的角色列表 collection
private List<Role> roleList;
}
d)テスト結果
/*
验证mybatis中的二级缓存
*/
@Test
public void testTwoCache() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession1 = sqlSessionFactory.openSession();
UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
// 第一次查询
User user = userMapper1.findById(1);
System.out.println(user);
// 只有执行sqlSession.commit或者sqlSession.close,那么一级缓存中内容才会刷新到二级缓存
sqlSession1.close();
SqlSession sqlSession2 = sqlSessionFactory.openSession();
UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
User user2 = userMapper2.findById(1);
System.out.println(user2);
sqlSession2.close();
}
2.3.3分析
第2レベルのキャッシュはマッパーマッピングレベルのキャッシュです。複数のSqlSessionが同じマッパーによってマップされたSQLステートメントを操作します。複数のSqlSessionが第2レベルのキャッシュを共有できます。第2レベルのキャッシュはSqlSession全体にあります。
2.3.4問題に注意を払う(ダーティリード)
Mybatisのセカンダリキャッシュは名前空間レベルであるため、マルチテーブルクエリが実行されるとダーティリードの問題が発生します
2.4まとめ
3つのMyBatisアノテーション
3.1MyBatisの一般的な注釈
過去数年で、アノテーション開発はますます人気が高まっており、Mybatisはアノテーション開発メソッドを使用することもできるため、マッパーマッピングファイルの書き込みを減らすことができます。最初にいくつかの基本的なCRUDについて学び、次に複雑なマッピングとマルチテーブル操作について学びます。
3.2 MyBatisコメントの追加、削除、変更、確認[キー]
3.2.1UserMapperインターフェースの作成
/*
查询用户
*/
@Select("select * from user")
public List<User> findAll();
/*
添加用户
*/
@Insert("insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})")
public void save(User user);
/*
更新用户
*/
@Update("update user set username = #{username},birthday=#{birthday} where id = #{id}")
public void update(User user);
/*
删除用户
*/
@Delete("delete from user where id = #{id}")
public void delete(Integer id);
3.2.2コア構成ファイルの書き込み
<!--我们使用了注解替代的映射文件,所以我们只需要加载使用了注解的Mapper接口即可-->
<mappers>
<!--扫描使用注解的Mapper类-->
<mapper class="com.lagou.mapper.UserMapper"></mapper>
</mappers>
<!--或者指定扫描包含映射关系的接口所在的包也可以-->
<mappers>
<!--扫描使用注解的Mapper类所在的包-->
<package name="com.lagou.mapper"></package>
</mappers>
3.2.3テストコード
public class MybatisTest {
private SqlSessionFactory sqlSessionFactory;
private SqlSession sqlSession;
// 下面的注解要在 @Test方法标注的方法执行之前来执行
@Before
public void before() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
sqlSession = sqlSessionFactory.openSession();
}
// 下面的注解要在 @Test方法标注的方法执行之后来执行
@After
public void after(){
sqlSession.commit();
sqlSession.close();
}
/*
测试查询方法
*/
@Test
public void testSelect() throws IOException {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> all = mapper.findAll();
for (User user : all) {
System.out.println(user);
}
}
/*
测试添加方法
*/
@Test
public void testInsert(){
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = new User();
user.setUsername("汤唯");
user.setSex("女");
user.setBirthday(new Date());
user.setAddress("北京");
mapper.save(user);
}
@Test
public void testUpdate(){
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = new User();
user.setUsername("柳岩真美");
user.setBirthday(new Date());
user.setId(9);
mapper.update(user);
}
@Test
public void testDelete(){
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mapper.delete(9);
}
}
3.3アノテーションを使用して複雑なマッピング開発を実現する
以前は、マッピングファイルで<resultMap>、<association>、および<collection>を構成することにより、複雑な関係マッピングを実装していました。
アノテーションを使用して開発した後、@ Results、@ Result、@ One、および@Manyアノテーションの組み合わせを使用して、複雑な関係の構成を完了することができます。
3.41対1のクエリ
3.4.1はじめに
デマンド:注文を照会すると同時に、注文が属するユーザーを照会します
1対1のクエリ
SELECT * FROM orders;
SELECT * FROM `user` WHERE id = #{订单的uid};
3.4.2コードの実装
a)OrderMapperインターフェース
/*
查询所有订单,同时查询订单所属的用户信息
*/
@Select("select * from orders")
@Results({ // 代替的就是resultMap标签 id标签 result标签
@Result(property = "id",column = "id",id = true), // id = true 表示当前是主键
@Result(property = "ordertime",column = "ordertime"),
@Result(property = "total",column = "total"),
@Result(property = "uid",column = "uid"),
@Result(property = "user",javaType = User.class,column = "uid",
one = @One(select = "com.lagou.mapper.UserMapper.findById",fetchType = FetchType.EAGER))
// 上面如果不设置fetchType, 查询结果中结果为null, 因为当前开了全局延迟加载
})
public List<Orders> findAllWithUser();
b)UserMapperインターフェース
/*
根据id查询用户
*/
@Select("select * from user where id = #{uid}")
public User findById(Integer uid);
c)テストコード
@Before
@After
/*
一对一查询(注解方式)
*/
@Test
public void testOneToOne(){
OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);
List<Orders> allWithUser = mapper.findAllWithUser();
for (Orders orders : allWithUser) {
System.out.println(orders);
}
}
3.51対多のクエリ
3.5.1はじめに
要件:ユーザーにクエリを実行すると同時に、ユーザーの注文をクエリします。1
対多のクエリステートメント
SELECT * FROM `user`;
SELECT * FROM orders where uid = #{用户id};
3.5.2コードの実装
a)UserMapperインターフェース
/*
查询所有用户,及关联的订单信息
*/
@Select("select * from user")
@Results({
@Result(property = "id",column = "id",id = true),
@Result(property = "username",column = "username"),
@Result(property = "birthday",column = "birthday"),
@Result(property = "sex",column = "sex"),
@Result(property = "address",column = "address"),
@Result(property = "ordersList",javaType = List.class,column = "id",
many = @Many(select = "com.lagou.mapper.OrderMapper.findOrderByUid",fetchType = FetchType.LAZY))
})
public List<User> findAllWithOrder();
b)OrderMapperインターフェース
/*
根据传递过来的用户id,查询该用户所具有的订单信息
*/
@Select("select * from orders where uid = #{uid}")
public List<Orders> findOrderByUid(Integer uid);
c)テストコード
/*
一对多查询(注解方式)
*/
@Test
public void testOneToMany(){
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> allWithOrder = mapper.findAllWithOrder();
for (User user : allWithOrder) {
System.out.println(user);
// 不加下面的语句, 订单信息为空, 因为开启了延迟加载
System.out.println(user.getOrdersList());
}
}
3.6多対多のクエリ
3.6.1はじめに
要件:すべてのユーザーを照会し、同時にユーザーのすべての役割を照会します
多対多のクエリステートメント
SELECT * FROM `user`;
SELECT * FROM role r INNER JOIN user_role ur ON r.`id` = ur.`rid`
WHERE ur.`uid` = #{用户id};
3.6.2コードの実装
a)UserMapperインターフェース
/*
查询所有用户及关联的角色信息
*/
@Select("select * from user")
@Results({
@Result(property = "id",column = "id",id = true),
@Result(property = "username",column = "username"),
@Result(property = "birthday",column = "birthday"),
@Result(property = "sex",column = "sex"),
@Result(property = "address",column = "address"),
@Result(property = "roleList",javaType = List.class,column = "id",
many = @Many(select = "com.lagou.mapper.RoleMapper.findAllByUid")),
})
public List<User> findAllWithRole();
b)RoleMapperインターフェース
/*
根据传递过来的用户id,查询该用户所具有的角色信息
*/
@Select("SELECT * FROM sys_role r INNER JOIN sys_user_role ur ON ur.roleid = r.id WHERE ur.userid = #{uid}")
public List<Role> findAllByUid(Integer uid);
c)テストコード
/*
多对多查询(注解方式)
*/
@Test
public void testManyToMany(){
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> allWithRole = mapper.findAllWithRole();
for (User user : allWithRole) {
System.out.println(user);
System.out.println(user.getRoleList());
}
}
3.7注釈ベースのセカンダリキャッシュ
3.7.1セカンダリキャッシュのサポートを有効にするようにSqlMapConfig.xmlファイルを構成します
<settings>
<!--
因为cacheEnabled的取值默认就为true,所以这一步可以省略不配置。
为true代表开启二级缓存;为false代表不开启二级缓存。
-->
<setting name="cacheEnabled" value="true"/>
</settings>
3.7.2注釈を使用して、マッパーインターフェイスで第2レベルのキャッシュを構成します
@CacheNamespace // 配置了二级缓存
public interface UserMapper { ... }
/*
测试注解实现二级缓存
*/
@Test
public void cacheTest(){
SqlSession sqlSession1 = sqlSessionFactory.openSession();
SqlSession sqlSession2 = sqlSessionFactory.openSession();
UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
User user1 = userMapper1.findById(1);
System.out.println(user1);
// 才能将内容从一级缓存刷新到二级缓存
sqlSession1.close();
User user2 = userMapper2.findById(1);
System.out.println(user2);
sqlSession2.close();
}
3.8アノテーションの遅延読み込み
1対1であろうと1対多であろうと、アノテーション構成にはfetchType属性があります。
3.9まとめ