Java开发工程师(Web方向) - 03.数据库开发 - 第5章.MyBatis

时间:2023-11-26 13:42:02

第5章--MyBatis

MyBatis入门

Abstract: 数据库框架的工作原理和使用方法(以MyBatis为例)

面向对象的世界与关系型数据库的鸿沟:

面向对象世界中的数据是对象;

关系型数据库中的数据是以行、列表示的二元表。

什么映射技术能实现对象和二元表之间的自动转换呢?

--ORM (Object Relation Mapping)

持久化类与数据库表之间的映射关系;对持久化对象的操作自动转换成对关系数据库操作。

关系数据库的每一行映射为每一个对象;关系数据库的每一列映射为对象的一个属性。

MyBatis:

项目前身为Apache基金会下的开源项目iBatis

支持自定义SQL、存储过程和高级映射的持久化框架

使用XML或注解配置

能映射基本数据元素、接口、Java对象到数据库

本质上是ORM框架,但不同的是:

不是直接建立Java对象到关系数据库表中数据之间的映射,而是建立对对象的操作方法到SQL语句之间的映射。

MyBatis功能架构:

Java开发工程师(Web方向) - 03.数据库开发 - 第5章.MyBatis

API接口层:主要提供了供外部使用的API,开发人员通过这些本地API来操作数据库;接口层接到调用请求以后,将请求转给数据处理层去完成具体的数据处理。

数据处理层:完成数据库的操作

基础支撑层:最基础的组件,为数据处理成提供支撑

MyBatis工作流机制:

在应用程序启动时,加载一个XML文件,该文件定义了后端数据库地址、SQL与Java之间的映射关系,根据XML或注解加载SQL语句、参数映射、结果映射到内存中;

应用程序调用API传入参数和SQL ID

MyBatis自动生成SQL语句完成数据库访问,转换执行结果返回应用程序,应用程序得到Java对象。

MyBatis环境搭建:

JAR: mybatis-3.4.5.jar: https://github.com/mybatis/mybatis-3/releases

(由于MyBatis的底层是基于JDBC实现数据库访问的,所以也需要JDBC的jar包)

MyBatis配置:

SqlSessionFactory: 通过该实例可以获取能够将对象转换成数据库的这么一个session

通过xml配置文件完成SqlSessionFactory的配置

MyBatis系统的核心配置:后端数据库连接实例的数据源、决定事务范围和控制方式的事务管理器 (transactionManager)

将该配置文件放置于工程的根目录下MyBatisTest/src/main/java/...xml

<?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>
<environments default="development">
<environment id="development">
<transactionManager type="jdbc"/>
<!-- 配置数据库连接信息 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost/helloworld" />
<property name="username" value="matt" />
<property name="password" value="matt" />
</dataSource>
</environment>
</environments>
</configuration>

transactionManager的type属性可以设置为jdbc或managed

jdbc: 表示MyBatis的事务控制其实是直接使用jdbc的提交和回滚

managed: 表示对于MyBatis的事务提交和回滚,MyBatis不会做任何事情,也不会调用jdbc的提交和回滚;事务的控制交给外部的容器,比如Spring的方式来完成

后端数据库源:和jdbc一样,包括driver, url, username, password.

完成了SqlSessionFactory的配置以后,就可以开始Java程序的编写了。

MyBatis本身是ORM框架,因此需要定义一些Java对象,建立对象对对象操作和sql语句之间的映射。

在类包MyBatisTest/src/package_name/中:创建User.java和GetUserInfo.java

i.e.

构造对象:

public class User {
private int id;
private String userName;
private String corp; public User(Integer id, String userName, String corp) {
this.id = id;
this.userName = userName;
this.corp = corp;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getCorp() {
return corp;
}
public void setCorp(String corp) {
this.corp = corp;
}
}

对对象的操作接口:

public interface GetUserInfo {
public User getUser(int id);
public void addUser(User user);
public void updateUser(User user);
public void deleteUser(User user);
}

完成了Java对象和对Java对象操作的接口的定义之后,

需要创建Java对象和SQL语句映射关系的配置文件

在类包MyBatisTest/src/package_name/中:创建userMaper.xml(自命名,之后注册到SqlSessionFactory.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">
<mapper namespace="com.micro.profession.mybatis.GetUserInfo">
<select id="getUser" parameterType="int"
resultType="com.micro.profesion.mybatis.User">
select id, userName, corp from MyBatisUser where id = #{id}
</select>
</mapper>

mapper标签中namespace属性:定义接口的操作类(interface name)

select标签的id:对对象的操作(getUser())

select标签的parameterType:传入值的类型(parameter of getUser())

select标签的resultType:要封装成的类(return from getUser())

上述完成了映射关系的配置文件,

接下来需要将这些配置文件注册到最开始的SqlSessionFactory配置文件中:

<configuration>
<environments>
...
</environments>
<mappers>
<mapper resource="com/micro/profession/mybatis/userMapper.xml"/>
</mappers>
</configuration>

完成数据库查询的步骤:

1. 加载配置文件(应用配置文件SqlSessionFactory.xml、关联映射文件userMapper.xml)

2. 生成SqlSessionFactory实例,获取SqlSession

3. 利用Session执行SQL

测试类:Test.java

public class Test {

    public static void main(String[] args) {

        // 1. 声明配置文件的目录地址
String resource = "SqlSessionFactory.xml"; // 2. 加载应用配置文件
InputStream is = Test.class.getClassLoader().getResourceAsStream(resource); // 3. 创建SqlSessionFactory
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(is); // 4. 获取Session
SqlSession session = sessionFactory.openSession();
try {
// 5. 获取操作类
GetUserInfo getUserInfo = session.getMapper(GetUserInfo.class); // 6. 查询操作
User user = getUserInfo.getUser(2);
System.out.println(user.getId() + ". " + user.getUserName() + ":" + user.getCorp());
} finally {
// 7. 关闭Session
session.close();
}
}
}

1. 因为配置文件在项目的根目录,于是路径直接写文件名即可。

2. 将配置文件用InputStream加载到程序中

3. 通过上述流对象,创建SqlSessionFactory的实例

4. 从SqlSessionFactory实例中获取session

5. 通过mapper,获得接口

6. 通过调用接口的方法,完成具体操作

7. finally,关闭session

以上程序即可运行,但是通过两个配置文件进行配置,会不会太繁琐了呢?

通过注解的方式进行配置:通过注解的方式进行声明接口与sql语句之间的映射,以替代映射文件

接口类文件GetUserInfoAnnotation.java (同上例的GetUserInfo.java)

public interface GetUserInfoAnnotation {

    @Select("select * from user where id = #{id}")
public User getUser(int id);
}

Test方法中需要配置映射关系:

Configuration conf = sessionFactory.getConfiguration();

conf.addMapper(GetUserInfoAnnotation.class);

public static void main(String[] args) {

        // 1. 声明配置文件的目录地址
String resource = "SqlSessionFactoryAnnotation.xml"; // 2. 加载应用配置文件
InputStream is = TestAnnotation.class.getClassLoader().getResourceAsStream(resource); // 3. 创建SqlSessionFactory
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(is);
// 完成配置映射关系 ******
Configuration conf = sessionFactory.getConfiguration();
conf.addMapper(GetUserInfoAnnotation.class); // 4. 获取Session
SqlSession session = sessionFactory.openSession();
try {
// 5. 获取操作类
GetUserInfoAnnotation getUserInfoAnnotation = session.getMapper(GetUserInfoAnnotation.class); // 6. 查询操作
User user = getUserInfoAnnotation.getUser(2);
System.out.println(user.getId() + ". " + user.getUserName() + ":" + user.getCorp());
} finally {
// 7. 关闭Session
session.close();
}
}

以上代码实现了之前mapper的作用(无需userMapper.xml文件,且在SqlSessionFactory.xml中无需添加mapper的注册)

添加增删改的功能:

实现接口中的addUser(User user); updateUser(User user); deleteUser(int id);功能

<insert id="addUser" parameterType="com.micro.profession.mybatis.User"
useGeneratedKeys="true" keyProperty="id">
insert into MyBatisUser (userName, Corp) values (#{userName}, #{Corp})
</insert> <update id="updateUser" parameterType="com.micro.profession.mybatis.User">
update MyBatisUser SET userName=#{userName}, Corp=#{Corp} WHERE id=#{id};
</update> <delete id="deleteUser" parameterType="int">
delete from MyBatisUser where id=#{id};
</delete>

useGeneratedKeys="true" 使用自增

keyProperty="id" 自增对应到id上

对应Test.java中:

改为SqlSessionFactory.openSession(true);

JDBC获取数据库连接时,默认自动进行提交,不是以事务为单位提交的;

而在MyBatis中,获取的事务默认是开启事务的,则默认对之后的每一个操作都是以事务的方式来提交的,需显式调用commit()

进行数据库操作:

public class Test {
public static void main(String[] args) {
// 1. 声明配置文件的目录地址
String resource = "SqlSessionFactory.xml"; // 2. 加载应用配置文件
InputStream is = Test.class.getClassLoader().getResourceAsStream(resource); // 3. 创建SqlSessionFactory
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(is); // 4. 获取Session
SqlSession session = sessionFactory.openSession();
try {
// 5. 获取操作类
GetUserInfo getUserInfo = session.getMapper(GetUserInfo.class); // 6. 数据库操作
// add user
User user = new User("XiaoMing", "Netease");
getUserInfo.addUser(user);
System.out.println("new ID: " + user.getId()); // id : useGeneratedKey // select user
user = getUserInfo.getUser(user.getId());
System.out.println(user.getId() + "." + user.getUserName() + ": " + user.getCorp()); // update user
user.setUserName("LiMing");
getUserInfo.updateUser(user);
user = getUserInfo.getUser(user.getId());
System.out.println(user.getId() + "." + user.getUserName() + ": " + user.getCorp()); // delete user
getUserInfo.deleteUser(user.getId());
user = getUserInfo.getUser(user.getId());
System.out.println(user); } finally {
// 7. 关闭Session
session.close();
}
}
}

MyBatis的优势和劣势

优势:入门门槛低;可以自己编写SQL使得更加灵活也可以进行SQL优化

劣势:需要自己编写SQL导致工作量大;数据库移植性差(需要修改sql语句)

MyBatis进阶

实际开发中会遇到复杂的数据库和复杂的SQL语句

i.e.

Java开发工程师(Web方向) - 03.数据库开发 - 第5章.MyBatis

上述ER图中User与Course之间的关系为many-to-many, 需要一个Associative Entity

Java开发工程师(Web方向) - 03.数据库开发 - 第5章.MyBatis

创建数据库:studentEnrollment

./mysql -u root -p;
mysql> create database studentEnrollment;
mysql> grant all privileges on studentEnrollment.* to matt@localhost;
mysql> flush privileges;
mysql> exit; ./mysql -u matt -p;
mysql> use studentEnrollment;
mysql> CREATE TABLE User (
-> id int(11) NOT NULL AUTO_INCREMENT,
-> userName varchar(100) NOT NULL,
-> corp varchar(100) DEFAULT NULL,
-> PRIMARY KEY (id)
-> ) ENGINE=InnoDB DEFAULT CHARSET=utf8; mysql> CREATE TABLE teacher (
-> id int(11) NOT NULL AUTO_INCREMENT,
-> teacherName varchar(100) NOT NULL,
-> PRIMARY KEY (id)
-> ) ENGINE=InnoDB DEFAULT CHARSET=utf8; mysql> CREATE TABLE course (
-> id int(11) NOT NULL AUTO_INCREMENT,
-> CourseName varchar(100) DEFAULT NULL,
-> teacher_id int(11) DEFAULT NULL,
-> PRIMARY KEY (id),
-> FOREIGN KEY (teacher_id) REFERENCES teacher(id)
-> ) ENGINE=InnoDB DEFAULT CHARSET=utf8; mysql> CREATE TABLE UserCourse (
-> id int(11) NOT NULL AUTO_INCREMENT,
-> user_id int(11) DEFAULT NULL,
-> course_id int(11) DEFAULT NULL,
-> PRIMARY KEY (id),
-> FOREIGN KEY (user_id) REFERENCES user(id),
-> FOREIGN KEY (course_id) REFERENCES course(id)
-> ) ENGINE=InnoDB DEFAULT CHARSET=utf8; mysql> INSERT INTO User VALUES (null, "XiaoMing", "Netease");
mysql> INSERT INTO Teacher VALUES (null, "Smith");
mysql> INSERT INTO Course VALUES (null, "math", 1);
mysql> INSERT INTO UserCourse VALUES (null, 1, 1);

创建对应的Java对象:User, Course, Teacher

public class User {
private int id;
private String userName;
private String corp;
private List<Course> courses; public User(int id, String userName, String corp) {
this.id = id;
this.userName = userName;
this.corp = corp;
}
}
// 还有其他getters和setters,省略(三个类都有)
public class Course {
private int id;
private String courseName;
private Teacher teacher;
// getters and setters
} public class Teacher {
private int id;
private String teacherName;
// getters and setters
}

创建对象的操作的接口:UserOperation.java

三张数据库表之间的连接查询

public interface UserOperation {
public User getUser(int id);
}

MyBatis中有很强大的元素 ResultMap:可以实现复杂查询结果到复杂对象关联关系的转化。

通过构造函数Constructor,在类实例化时注入结果:一般在关联的时候使用

idArg:ID参数,标记结果作为ID可以帮助提高整体效能

arg:注入到构造方法的一个普通结果

在mapper配置文件userMapperConstructor.xml中

<resultMap id="UserMap" type="com.micro.profession.mybatis.resultmaptest.User">
<constructor>
<idArg column="id" javaType="int" />
<arg column="userName" javaType="String" />
<arg column="corp" javaType="String" />
</constructor>
</resultMap>

要求在User类中的构造函数的参数必须和这里的column相同。(courses没有进行初始化)

此时若要映射getUser(),可以这么写:

<select id="getUser" parameterType="int" resultMap="UserMap">
select * from user where id = #{id}
</select> 

其中,resultMap="UserMap"中的"UserMap"就是上面定义的resultMap的id

不要忘记写上SqlSessionFactory.xml哦~

到这里,已经完成了从Java类到具体字段之间的映射关系。

测试:Test.java

public class Test {

    public static void main(String[] args) {
String resource = "SqlSessionFactory.xml";
InputStream is = Test.class.getClassLoader().getResourceAsStream(resource);
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(is);
SqlSession session = sessionFactory.openSession(true); try {
UserOperation userOperation = session.getMapper(UserOperation.class);
User user = userOperation.getUser(1);
System.out.println(user.getId() +"." + user.getUserName() + ":" + user.getCorp()); } finally {
session.close();
}
}
}

但是报错:

Java开发工程师(Web方向) - 03.数据库开发 - 第5章.MyBatis

解决方案:将User的构造函数改为public User (Integer id, String userName, String corp) {} 即可

(原来是 public User (int id, String userName, String corp) {} )

但如何读取User中的courses呢?-- 解决容器问题

使用resultMap中的Collection标签:实现一对多的关联(顾名思义:collection (a collection of complex types))

id:一个ID结果,标记结果作为ID可以帮助提高整体效能

result: 注入到字段或JavaBean属性的普通结果

(Source: The only difference between the two is that id will flag the result as an identifier property to be used when comparing object instances. This helps to improve general performance, but especially performance of caching and nested result mapping (i.e. join mapping).)

在mapper.xml中的<resultMap></resultMap>中加入<collection>标签:(与<constructon>同级)

<collection property="courses"
ofType="com.micro.profession.mybatis.resultmaptest.Course">
<id property="id" column="courseId" />
<result property="courseName" column="courseName" />
</collection>

property的值"courses"必须跟要关联的User类中的List<Course> courses变量名一致

ofType的值为该collection的类型名

id/result中的property为course对象中的变量名id;column为database返回结果中的column

修改select语句:

select u.id as userId, userName, courseName, corp, c.id as courseId from user u left join
userCourse uc on u.id=uc.user_id left join
course c on c.id=uc.course_id
where u.id = #{id}

三张表User, Course, UserCourse之间的关联,用left join合并

N.B. 由于select语句中使用了u.id as userId这个alias,所以需要将resultMap.constructor中的id column也改为"userID")

(which takes me nearly twenty minutes to fix the bug);

http://www.mybatis.org/mybatis-3/sqlmap-xml.html

Test.java中

System.out.println(user.getCourses().get(0).getCourseName()); // 得到结果"math"

Course和Teacher也有关联,那如何实现这个呢?

Association:实现复杂类型之间的关联

id, result:与collection中的功能相同

select语句:

select u.id as userId, userName, courseName, corp, c.id as courseId, teacherName, t.id as teacherId
from user u left join
userCourse uc on u.id=uc.user_id left join
course c on c.id=uc.course_id left join
teacher t on t.id=c.teacher_id
where u.id = #{id}

在collection中的末尾加上association tag:(因为teacher是与course关联的,而course在此为collection中的元素)

<collection property="courses" ...>
...
<association property="teacher" column="teacher_id" javaType="com.micro.profession.mybatis.resultmaptest.Teacher">
<id property="id" column="teacherId" />
<result property="teacherName" column="teacherName" />
</association>
</collection> 

property="teacher"对应Course类中的Teacher teacher属性名

column="teacher_id"对应数据库中返回结果的表字段teacher_id

id/result:与collection中功能相同

Test.java中:

System.out.println(user.getCourses().get(0).getTeacher().getTeacherName());

DataSource:数据库连接池

在MyBatis 3.0中内置了连接池,与DBCP类似

在SqlSessionFactory.xml的environment中设置DataSource type="POLLED"即为开启连接池

Java开发工程师(Web方向) - 03.数据库开发 - 第5章.MyBatis

有空闲连接链和活跃连接链两个队列,当程序需要数据库连接时,首先会判断如果idleConnections中有空闲连接则直接分配并执行sql;如果没有,则会判断activeConnections队列中是否超过了最大活跃连接数的限制,如果没有,就会创建新的连接加入activeConnections中;如果超过了,就会遍历activeConnections队列找到最早的连接判断是否超过poolMaximunCheckoutTime,如果过期则强制使其失效并返回该连接。

连接池常用配置选项:

poolMaximunActiveConnections: 最大活跃连接数(随着连接数增加,性能可能达到拐点,不建议设置过大))

poolMaximunIdleConnections: 最大空闲连接数(建议设置与poolMaximun相同即可)

poolMaximunCheckoutTime:在idleConnections为空且activeConnections达到最大值时检查(详细步骤见上)

建议设置为预期最大SQL执行时间(2~3分钟即可)

poolTimeToWait:获取服务器端数据库连接的超时时间,若超过则打印日志,并重新获取。建议使用默认值20s

连接失效:若有连接长期空闲,服务器端会把该连接关闭,而连接池不知道该连接被关闭,依然会使用该连接,则会抛出异常)

poolPingEnabled:启用连接侦测,检查连接池中的连接是否为有效连接(默认关闭,建议启用)

poolPingQuery:侦测sql来探测数据库是否存活,建议使用select 1,因为开销小

poolPingConnectionsNotUsedFor:侦测时间,建议小于服务器端超时时间,MySQL默认8小时(则一定要小于8)

MyBatis 单元测试

本次得分为:100.00/100.00, 本次测试的提交时间为:2017-08-31
1判断(10分)

ORM框架实现了关系数据库中二元表与面向对象的世界中对象的映射转换,通过ORM框架,我们可以在面向对象的编程中,通过调用对象的方法实现对关系数据库中数据的修改。

  • A.√10.00/10.00
  • B.×
2判断(10分)

关系数据库中的每一列映射为每一个Java对象,每一行映射为Java对象的一个属性。

  • A.√
  • B.×10.00/10.00
3判断(10分)

MyBatis的前身apache 基金会下的开源项目iBatis。

  • A.√10.00/10.00
  • B.×
4判断(10分)

MyBatis可以实现自动生成SQL语句,开发者无需自己编写SQL语句。

  • A.√
  • B.×10.00/10.00
5判断(10分)

SqlSessionFactory是基于MyBatis的应用中心,通过SqlSessionFactory实例可以配置后端数据库的数据源和决定事务范围和控制方式的的事务管理器。

  • A.×
  • B.√10.00/10.00
6判断(10分)

通过注解的方式可以配置SQL语句和对象方法之间的映射。

  • A.√10.00/10.00
  • B.×
7判断(10分)

association标签可以帮助我们实现通过构造函数的方式对复杂对象注入值。

  • A.√
  • B.×10.00/10.00
8判断(10分)

ResultMap是MyBatis最强大的元素,可以实现复杂查询结果到复杂对象的映射。

  • A.√10.00/10.00
  • B.×
9判断(10分)

MyBatis 3.0内置了数据库连接池,将datasource type置为pooled,启用数据库连接池。

  • A.√10.00/10.00
  • B.×
10判断(10分)

MyBatis 内置连接池维护了空闲连接和活跃连接两个队列,当应用需要执行SQL时,首先从活跃连接队列中获取连接,如果获取不到,则创建新的连接,然后加入到活跃连接队列中。

  • A.√
  • B.×10.00/10.00

MyBatis 作业

MyBatis课程单元作业,同学们需要认真完成MyBatis入门和进阶两门课程后,完成该作业题目。作业内容为一道编程题目。

依照学术诚信条款,我保证此回答为本人原创,所有回答中引用的外部材料已经做了出处标记。

1(100分)

有一个在线交易的电商平台,主要包括三张数据库业务表:

Java开发工程师(Web方向) - 03.数据库开发 - 第5章.MyBatis

现在需要基于MyBatis数据库ORM框架,实现读取商品信息和用户信息两个功能。

商品对象和用户对象定义如下:

Java开发工程师(Web方向) - 03.数据库开发 - 第5章.MyBatis

Java开发工程师(Web方向) - 03.数据库开发 - 第5章.MyBatis

每个用户都关联了一个Product的List,用于表示该用户所购买的所有商品,可以通过查询transaction表的交易记录获得。

操作接口定义如下:

Java开发工程师(Web方向) - 03.数据库开发 - 第5章.MyBatis

请分别实现getProduct和getUser两个函数方法与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 namespace="MyBatisTest.Op">
<select >
</select>
</mapper>

答:

1. 创建数据库

CREATE TABLE Product (
Id int AUTO_INCREMENT PRIMARY KEY,
ProductName varchar(100) NOT NULL,
Catalog varchar(100) DEFAULT NULL
) ENGINE=Innodb; CREATE TABLE User (
Id int AUTO_INCREMENT PRIMARY KEY,
UserName varchar(100) NOT NULL,
Tel varchar(11) DEFAULT NULL
) ENGINE=InnoDB; CREATE TABLE Transaction (
Id int AUTO_INCREMENT PRIMARY KEY,
UserId int NOT NULL,
ProductId int NOT NULL,
FOREIGN KEY (UserId) REFERENCES User(Id),
FOREIGN KEY (ProductId) REFERENCES Product(Id)
) ENGINE=InnoDB; INSERT INTO Product VALUES (null, "Product1", "Catalog1");
INSERT INTO User VALUES (null, "User1", null);
INSERT INTO Transaction VALUES (null, 1, 1);

2. 导入jdbc.jar和mybatis.jar

3. 创建src下包com.sheng.mybatis.assignment; src下的SqlSessionFactory.xml;

包中 Product.java & User.java & Op.java(如题), Test.java, 还有映射配置文件mapper.xml

4. SqlSessionFactory.xml

<?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>
<environments default="development">
<environment id="development">
<transactionManager type="jdbc"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost/assi_ecommerce"/>
<property name="username" value="matt"/>
<property name="password" value="matt"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/sheng/mybatis/assignment/mapper.xml"/>
</mappers>
</configuration>

5. mapper.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">
<mapper namespace="com.sheng.mybatis.assignment.Op">
<select id="getUser" parameterType="int" resultMap="UserMap">
select u.id as userId, userName, tel, p.id as productId
from user u left join Transaction t on t.UserId = u.id
left join Product p on p.id = t.productId
where u.id=#{id}
</select> <select id="getProduct" parameterType="int"
resultType="com.sheng.mybatis.assignment.Product">
select * from Product where id = #{id}
</select> <resultMap id="UserMap" type="com.sheng.mybatis.assignment.User">
<constructor>
<idArg column="userId" javaType="int" />
<arg column="userName" javaType="String" />
<arg column="tel" javaType="String" />
</constructor>
<collection property="products" ofType="java.lang.Integer">
<result property="" column="productId" />
</collection>
</resultMap>
</mapper>

6. Test.java

public class Test {

    public static void main(String[] args) {

        String resource = "SqlSessionFactory.xml";
InputStream is = Test.class.getClassLoader().getResourceAsStream(resource);
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(is);
SqlSession session = sessionFactory.openSession(); try {
Op op = session.getMapper(Op.class);
// test getUser()
User user = op.getUser(1);
System.out.println(user.getUserName() + " purchased: " + user.getProducts());
// test getProduct()
for (int productId: user.getProducts()) {
Product product = op.getProduct(productId);
System.out.println("productId:" + productId + " " + product.getProductName());
} } finally {
session.close();
}
}
}

7. User.java的改动:增加一个构造函数

public User(Integer id, String userName, String tel) {
super();
this.id = id;
this.userName = userName;
this.tel = tel;
}

8. Output

Java开发工程师(Web方向) - 03.数据库开发 - 第5章.MyBatis