11.2 注解的使用示例2 一对一关联关系
目录结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| G:\workspace_web2\MyAOneToOneTest ├─src │ ├─db.properties │ ├─domain │ │ ├─Card.java │ │ └─Person.java │ ├─fractory │ │ └─SqlSessionFratoryTools.java │ ├─log4j.xml │ ├─mapper │ │ ├─CardMapper.java │ │ └─PersonMapper.java │ ├─mybatis-config.xml │ ├─mybatis_tb_card_tb_person.sql │ └─test │ └─OneToOneTest.java └─WebContent ├─META-INF └─WEB-INF └─lib ├─ant-1.9.6.jar ├─ant-launcher-1.9.6.jar ├─asm-5.2.jar ├─cglib-3.2.5.jar ├─commons-logging-1.2.jar ├─javassist-3.22.0-CR2.jar ├─log4j-1.2.17.jar ├─log4j-api-2.3.jar ├─log4j-core-2.3.jar ├─mybatis-3.4.5.jar ├─mysql-connector-java-5.1.44-bin.jar ├─ognl-3.1.15.jar ├─slf4j-api-1.7.25.jar └─slf4j-log4j12-1.7.25.jar
|
创建数据库表
首先,给之前创建的mybatis
数据库创建两个表tb_card
和tb_person
,并插入测试数据。SQL
脚本如下使用方式:
保存成.sql
文件,然后使用Navicat
导入(设置编码格式utf-8
,免得乱码)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| DROP TABLE IF EXISTS `tb_card`; CREATE TABLE `tb_card` ( `id` int(11) NOT NULL AUTO_INCREMENT, `code` varchar(18) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT INTO `tb_card` VALUES ('1', '43280119980091');
DROP TABLE IF EXISTS `tb_person`; CREATE TABLE `tb_person` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(18) NOT NULL, `sex` varchar(18) NOT NULL, `age` int(11) DEFAULT NULL, `card_id` int(11) DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `card_id` (`card_id`), CONSTRAINT `tb_person_ibfk_1` FOREIGN KEY (`card_id`) REFERENCES `tb_card` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT INTO `tb_person` VALUES ('1', '小明', '男', '22', '1');
|
创建持久化类
Card.java
/MyAOneToOneTest/src/domain/Card.java1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| package domain; public class Card { private Integer id; private String code; public Card() { } public Card(Integer id, String code) { this.id = id; this.code = code; } @Override public String toString() { return "Card [id=" + id + ", code=" + code + "]"; } }
|
Person.java
/MyAOneToOneTest/src/domain/Person.java1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| package domain; public class Person { private Integer id; private String name; private String sex; private Integer age; private Card card; public Person() { super(); } @Override public String toString() { return "Person [id=" + id + ", name=" + name + ", sex=" + sex + ", age=" + age + ", card=" + card + "]"; } }
|
mapper接口
CardMapper.java
/MyAOneToOneTest/src/mapper/CardMapper.java1 2 3 4 5 6 7 8 9
| package mapper;
import org.apache.ibatis.annotations.Select; import domain.Card;
public interface CardMapper { @Select("select * from tb_card where id=#{id}") Card selectCardById(Integer id); }
|
PersonMapper.java
/MyAOneToOneTest/src/mapper/PersonMapper.java1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| package mapper;
import org.apache.ibatis.annotations.One; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Result; import org.apache.ibatis.annotations.Results; import org.apache.ibatis.annotations.Select; import org.apache.ibatis.mapping.FetchType; import domain.Person;
public interface PersonMapper { @Select("select * from tb_person where id=#{id}") @Results({ @Result(column = "id",property = "id", id = true), @Result(column = "name",property = "name"), @Result(column = "sex",property = "sex"), @Result(column = "age",property = "age"), @Result(column = "card_id",property = "card", one = @One(select = "mapper.CardMapper.selectCardById", fetchType = FetchType.LAZY) ) }) Person selectPersonById(@Param("id") Integer id); }
|
selectPersonById
方法使用了@Select
注解,其根据id
查询对应的Person
数据。因为需要将Person
对应的Card
数据也查询出来,所以Person
的Card
属性使用了一个@Result
结果映射。column="card_id"
,property="card"
表示Person
的Card
属性对应tb_person
表的card_id
列,one
属性表示是一个一对一关联关系,
@one
注解的select
属性表示需要关联执行的SQL
语句,
fetchType
表示查询的类型是立即加载(EAGER
)还是懒加载(LAZY
)。
测试类
/MyAOneToOneTest/src/test/OneToOneTest.java1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| package test;
import org.apache.ibatis.session.SqlSession; import domain.Person; import fractory.SqlSessionFactoryTools; import mapper.PersonMapper;
public class OneToOneTest { public static void main(String[] args) { SqlSession sqlSession = null; try { sqlSession = SqlSessionFactoryTools.getSqlSession(); PersonMapper personMapper = sqlSession.getMapper(PersonMapper.class); Person person = personMapper.selectPersonById(1); testLazyUseCardNow(person); sqlSession.commit(); } catch (Exception e) { sqlSession.rollback(); e.printStackTrace(); } finally { if (sqlSession != null) sqlSession.close(); } }
private static void testLazyUseCardNow(Person person) { System.out.println(person.toString()); }
private static void testLazyUseCardLazy(Person person) { System.out.println(person.toStringSimple()); System.out.println("================================="); System.out.println(person.getCard()); } }
|
运行效果
运行OneToOneTest
类的main
方法,该方法通过SqlSession
的getMapper(Class<T> type)
方法获得mapper
接口的代理对象PersonMapper
。调用PersonMapper
的selectPersonById
方法时会执行该方法上的注解。需要注意的是,Person
的一对一关联使用的注解@one
的select
属性,要执行的SQL
语句在CardMapper
接口的selectCardById
方法的注解中。控制台显示如下所示:
1 2 3 4 5 6 7
| DEBUG [main] ==> Preparing: select * from tb_person where id=? DEBUG [main] ==> Parameters: 1(Integer) DEBUG [main] <== Total: 1 DEBUG [main] ==> Preparing: select * from tb_card where id=? DEBUG [main] ==> Parameters: 1(Integer) DEBUG [main] <== Total: 1 Person [id=1, name=小明, sex=男, age=22, card=Card [id=1, code=450101199701243862]]
|
可以看到,查询Person
信息时Person
对应的Card
对象也被查询出来了。
在上面PersonMapper接口中的设置的是懒加载。
由于Person类的toString方法里面有访问到card属性,所以会执行关联查询。
懒加载验证
如果把上面的testLazyUseCardNow(person);语句注释掉,打开testLazyUseCardLazy(person);语句。如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| public static void main(String[] args) { SqlSession sqlSession = null; try { sqlSession = SqlSessionFactoryTools.getSqlSession(); PersonMapper personMapper = sqlSession.getMapper(PersonMapper.class); Person person = personMapper.selectPersonById(1); testLazyUseCardLazy(person); sqlSession.commit(); } catch (Exception e) { sqlSession.rollback(); e.printStackTrace(); } finally { if (sqlSession != null) sqlSession.close(); } }
|
则先执行一条SQL语句,取出Person,当需要访问Person类的card成员时,才会执行下一条SQL语句。
运行结果如下:
1 2 3 4 5 6 7 8 9
| DEBUG [main] ==> Preparing: select * from tb_person where id=? DEBUG [main] ==> Parameters: 1(Integer) DEBUG [main] <== Total: 1 Person [id=1, name=小明, sex=男, age=22] ================================= DEBUG [main] ==> Preparing: select * from tb_card where id=? DEBUG [main] ==> Parameters: 1(Integer) DEBUG [main] <== Total: 1 Card [id=1, code=450101199701243862]
|
这就是懒加载的效果。
立即加载验证
再PersonMapper接口中使用立即加载
public interface PersonMapper {
@Select("select * from tb_person where id=#{id}")
@Results({
@Result(column = "id",property = "id", id = true),
@Result(column = "name",property = "name"),
@Result(column = "sex",property = "sex"),
@Result(column = "age",property = "age"),
@Result(column = "card_id",property = "card",
one = @One(select = "mapper.CardMapper.selectCardById", fetchType = FetchType.EAGER)
)
})
Person selectPersonById(@Param("id") Integer id);
}
再次运行上面的测试类,运行结果如下:
1 2 3 4 5 6 7 8 9
| DEBUG [main] ==> Preparing: select * from tb_person where id=? DEBUG [main] ==> Parameters: 1(Integer) DEBUG [main] ====> Preparing: select * from tb_card where id=? DEBUG [main] ====> Parameters: 1(Integer) DEBUG [main] <==== Total: 1 DEBUG [main] <== Total: 1 Person [id=1, name=小明, sex=男, age=22] ================================= Card [id=1, code=450101199701243862]
|
可以看到在立即加载模式下,就算还没有访问Person类的card属性,也会执行关联查询,加载card内容。