mybatis入门篇2 --- mybatis的部分配置信息以及连表查询,分步查询
接下来看一下我们的mybatis的进一步操作,熟悉一下相关配置信息,以及多参数查询,连表查询,以及分布查询的功能。
首先mybatis的中文文档就是:https://mybatis.org/mybatis-3/zh/configuration.html#environments
首先看一下三个数据库表,user,order,user_order,这是一个多对多关系。
userId对应user表的id, orderId对应order表的id
本次对于user表没有记性一对多的操作,仅查询user表信息,对于order操作进行操作一对多关系。多对多就是分别的一对多
首先看一下项目结构,lib包里面的jar包不变
utils里面的类跟之前一样,不再展示。
看一下我们的db.properties,这个就是指定我们的数据库连接信息,
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8 jdbc.username=root jdbc.password=mysql
接下来看一下我们配置信息,如果其他信息,可以到上面的文档中继续查找
<?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> <!-- MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。 配置文档的顶层结构如下 properties(属性) settings(设置) typeAliases(类型别名) typeHandlers(类型处理器) objectFactory(对象工厂) plugins(插件) environments(环境配置) environment(环境变量) transactionManager(事务管理器) dataSource(数据源) databaseIdProvider(数据库厂商标识) mappers(映射器) --> <!--定义属性以及读取属性文件--> <properties resource="db.properties" /> <!--这个就是初始化配置--> <settings> <!--配置sql打印,将sql打印到控制台--> <setting name="logImpl" value="STDOUT_LOGGING"/> <!--开启驼峰命名法,默认是false,一旦开启true,那么必须符合所有的驼峰体与数据链蛇形字体的对应,否则无法进行赋值--> <setting name="mapUnderscoreToCamelCase" value="false"/> <!--延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。就是我们当时的那个collection里面,如果不进行调用user,那么就不会打印信息--> <setting name="lazyLoadingEnabled" value="true"/> <!--当开启时,任何方法的调用都会加载该对象的所有属性。否则,每个属性会按需加载--> <setting name="aggressiveLazyLoading" value="false"/> <!--指定哪个对象的方法触发一次延迟加载。--> <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/> </settings> <!--定义别名--> <typeAliases> <!--单个别名的定义--> <!-- <typeAlias alias="User" type="com.yang.domain.User" />--> <!--批量定义别名,别名就是定义的类名--> <package name="com.yang.domain" /> </typeAliases> <!-- 这个就是配置换奖,其中default可以指定选择哪个环境,这个也可以在创建连接工厂时指定 SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment); --> <environments default="development"> <!--这个是开发环境配置--> <environment id="development"> <!--使用事务--> <transactionManager type="JDBC" /> <!-- 这个指定数据库源, pooled这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。 这是一种使得并发 Web 应用快速响应请求的流行处理方式。, UNPOOLED这个数据源的实现只是每次被请求时打开和关闭连接, 不同的数据库在性能方面的表现也是不一样的,对于某些数据库来说,使用连接池并不重要, --> <!--从文件加载的数据库配置--> <dataSource type="POOLED"> <!--这是 JDBC 驱动的 Java 类的完全限定名(并不是 JDBC 驱动中可能包含的数据源类--> <property name="driver" value="${jdbc.driver}" /> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> </dataSource> <!--写死的数据库配置--> <!-- <dataSource type="UNPOOLED">--> <!-- <!–这是 JDBC 驱动的 Java 类的完全限定名(并不是 JDBC 驱动中可能包含的数据源类–>--> <!-- <property name="driver" value="com.mysql.jdbc.Driver" />--> <!-- <property name="url" value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8" />--> <!-- <property name="username" value="root" />--> <!-- <property name="password" value="mysql" />--> <!-- </dataSource>--> </environment> <!--这个是上线环境配置--> <environment id="prod"> <transactionManager type="JDBC" /> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8" /> <property name="username" value="root" /> <property name="password" value="#QAZwsx" /> </dataSource> </environment> </environments> <!--加载映射文件--> <mappers> <!--这个是之前使用的定义单个文件映射--> <!-- <mapper resource="com/yang/domain/Customer.xml" />--> <!--现在使用包结构定义映射文件,需要遵从下面规范 1。名称必须要跟接口名称一致 2。必须和mapper接口在同一个目录 --> <package name="com.yang.mapper" /> </mappers> </configuration>
看一下javabean
// user public class User { private Integer id; private String username; private String password; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Override public String toString() { return "User{" + "id=" + id + ", username=‘" + username + ‘\‘‘ + ", password=‘" + password + ‘\‘‘ + ‘}‘; } } // order public class Order { private Integer id; private String name; private List<User> users = new ArrayList<>(); public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public List<User> getUsers() { return users; } public void setUsers(List<User> users) { this.users = users; } @Override public String toString() { return "Order{" + "id=" + id + ", name=‘" + name + ‘\‘‘ + ", users=" + users + ‘}‘; } }
接下来,我们看一下mapper的编写,首先看一下基本规范
- namespace必须和Mapper接口类路径一致
- id必须和Mapper接口方法名一致
- parameterType必须和接口方法参数类型一致
- resultType必须和接口方法返回值类型一致
我们看一下UserMapper,就是一个接口类,我们使用debug看源码时,发现有使用invoke这个方法,并且jar包有cglib,这就是反射来帮助我们进行处理的,记住多参数传值必须指定param的值,否则就需要按顺序使用param1,param2.。。。
package com.yang.mapper; import com.yang.domain.User; import org.apache.ibatis.annotations.Param; import java.util.List; import java.util.Map; public interface UserMapper { /* mapper接口编写规则: 1.namespace必须和Mapper接口类路径一致 2.id必须和Mapper接口方法名一致 3.parameterType必须和接口方法参数类型一致 4.resultType必须和接口方法返回值类型一致 **/ // 通过指定的ID来获取user对象 User getUserById(Integer id); // 获取全部用户 List<User> getUsers(); // 新建用户 void insertUser(User user); // 删除用户,可以通过指定param的value来确定xml文件中使用的参数名称 void deleteUser(@Param("id") Integer id); // 将类按照map格式返回 Map<String, Object> getUserMapById(@Param("id") Integer id); // 多参数传递 User getUserByArgs(@Param("id") Integer id, @Param("username") String username); // 结果集使用resultMap User getResultMapById(@Param("id") Integer id); // 根据用户订单关系获取用户 User getUserThoughRelation(@Param("orderId") Integer orderId); }
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"> <!--这个namespace必须与映射的类接口一致--> <mapper namespace="com.yang.mapper.UserMapper"> <!--因为我们之前配置了别名,所以可以直接使用User来代替com.yang.domain.User--> <select id="getUserById" resultType="User"> select * from `user` where id = #{id} </select> <!--获取全部用户--> <select id="getUsers" resultType="com.yang.domain.User"> select * from `user`; </select> <!-- useGeneratedKeys="true" keyProperty="id" keyColumn="id" 这是另一种方法获取id值 这个跟之前写的是一样的 <selectKey keyColumn="id" keyProperty="id" resultType="Integer" order="AFTER"> select last_insert_id() </selectKey> --> <insert id="insertUser" useGeneratedKeys="true" keyProperty="id" keyColumn="id"> insert into `user` (username, password) values (#{username}, #{password}); </insert> <!--parameterType这个可以不用声明--> <delete id="deleteUser"> -- delete from `user` where id=${id} delete from `user` where id=#{id} </delete> <!--第一种传参方法,这个不安全,容易被sql注入--> <!--==> Preparing: select * from `user` where id=2 --> <select id="getUserMapById" resultType="java.util.Map"> select * from `user` where id=${id} </select> <!--第二种查询方法,这种可以放置sql注意,可以看出这个使用占位符插入数据--> <!--==> Preparing: select * from `user` where id=? --> <!-- <select id="getUserMapById" resultType="java.util.Map">--> <!-- select * from `user` where id=#{id} --> <!-- </select>--> <!--这个就是传递多参数,必须使用我们当时定义的那个--> <select id="getUserByArgs" resultType="com.yang.domain.User"> select * from `user` where id=#{id} and username=#{username} </select> <resultMap id="userMap" type="User"> <id column="id" property="id" /> <result column="username" property="username" /> <result column="password" property="password" /> </resultMap> <!--使用resultMap指定乐行,column代表数据库字段,property代表java类字段--> <select id="getResultMapById" resultMap="userMap"> select * from `user` where id=#{id} </select> <!--根据用户订单关系表获取用户--> <select id="getUserThoughRelation" resultType="com.yang.domain.User"> select * from `user` where id in(select userId from `user_order` where orderId = #{orderId}) </select> </mapper>
接下来,我们需要看一下一对多查询,一对多查询我们可以使用一条sql语句连表查询出来,并且封装一个resultMap来进行赋值结果,或者使用分布查询,来实现,建议使用分步查询,尽量少使用连表查询
OrderMapper
package com.yang.mapper; import com.yang.domain.Order; import org.apache.ibatis.annotations.Param; import java.util.List; public interface OrderMapper { // 插入一条order void insertOrder(Order order); // 插入关系表 void insertRelation(@Param("userId") Integer userId, @Param("orderID") Integer orderId); // 获取所有订单 List<Order> getAllOrder(); // 获取单个订单 Order getOrderById(Integer id); }
OrderMapper.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"> <!--这个namespace必须与映射的类接口一致--> <mapper namespace="com.yang.mapper.OrderMapper"> <!--插入订单信息,并返回id--> <insert id="insertOrder" useGeneratedKeys="true" keyColumn="id" keyProperty="id"> insert into `order`(name) values (#{name}) </insert> <!--插入关系表--> <insert id="insertRelation"> insert into `user_order`(userId, orderId) values (#{userId}, #{orderID}) </insert> <resultMap id="ordersMap" type="Order"> <!--这个是指定字段, 对于主键可以使用id或者result,其他字段只能使用resule--> <id column="id" property="id"/> <result column="name" property="name"/> <!--这个字段对应的是用户列表。因此使用collection进行赋值,指定type是list,里面的字段是User类型,然后在里面在指定赋值字段--> <collection property="users" javaType="list" ofType="User"> <id column="userId" property="id"/> <result column="username" property="username"/> <result column="password" property="password"/> </collection> </resultMap> <!--通过连表查询查处所有数据--> <select id="getAllOrder" resultMap="ordersMap"> select * from `order` as o left join `user_order` as uo on o.id = uo.orderId left join `user` as u on uo.userId = u.id </select> <resultMap id="orderMap" type="Order"> <id column="id" property="id"/> <result column="name" property="name"/> <collection property="users" javaType="list" ofType="Order" select="com.yang.mapper.UserMapper.getUserThoughRelation" column="id" /> </resultMap> <!--分布查询--> <select id="getOrderById" resultMap="orderMap"> select * from `order` where id = #{id} </select> </mapper>
接下来看一下我们的测试
package com.yang.test; import com.yang.domain.Order; import com.yang.domain.User; import com.yang.mapper.OrderMapper; import com.yang.mapper.UserMapper; import com.yang.utils.MyBatisUtils; import org.apache.ibatis.session.SqlSession; import org.junit.Test; import java.util.List; import java.util.Map; public class MyTest { @Test public void getUser() { // 建立sql连接会话 SqlSession sqlSession = MyBatisUtils.openSession(); // 获取用户映射 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); // 通过定义的接口进行查询数据库 User user = userMapper.getUserById(1); System.out.println(user); // User{id=1, username=‘yang‘, password=‘1234‘} List<User> users = userMapper.getUsers(); for (User user1 : users) { System.out.println(user1); /* User{id=1, username=‘yang‘, password=‘1234‘} User{id=2, username=‘shi‘, password=‘5678‘} User{id=3, username=‘xiong‘, password=‘9012‘} */ } // 增加用户 User newUser = new User(); newUser.setUsername("mark"); newUser.setPassword("1111"); userMapper.insertUser(newUser); System.out.println(newUser); // User{id=7, username=‘mark‘, password=‘1111‘} sqlSession.commit(); // 删除用户 userMapper.deleteUser(8); sqlSession.commit(); // 关闭连接 sqlSession.close(); } @Test public void getUserMap() { // 建立sql连接会话 SqlSession sqlSession = MyBatisUtils.openSession(); // 获取用户映射 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); // 通过定义的接口进行查询数据库 Map<String, Object> user = userMapper.getUserMapById(2); System.out.println(user); // {password=5678, id=2, username=shi} // 关闭连接 sqlSession.close(); } @Test public void getUseByArgs() { // 建立sql连接会话 SqlSession sqlSession = MyBatisUtils.openSession(); // 获取用户映射 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); // 通过定义的接口进行查询数据库 User user = userMapper.getUserByArgs(2, "shi"); System.out.println(user); // User{id=2, username=‘shi‘, password=‘5678‘} // 关闭连接 sqlSession.close(); } @Test public void getResultMap() { // 建立sql连接会话 SqlSession sqlSession = MyBatisUtils.openSession(); // 获取用户映射 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); User user = userMapper.getResultMapById(2); System.out.println(user); // User{id=2, username=‘shi‘, password=‘5678‘} sqlSession.close(); } @Test public void orders() { // 建立sql连接会话 SqlSession sqlSession = MyBatisUtils.openSession(); // 获取用户映射 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); // 获取订单映射 OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class); User user = userMapper.getUserById(3); Order order = new Order(); order.setName("订单三"); order.getUsers().add(user); // 添加订单 orderMapper.insertOrder(order); // 添加关系映射 orderMapper.insertRelation(user.getId(), order.getId()); // 提交事务 sqlSession.commit(); // 查询所有的订单 List<Order> allOrder = orderMapper.getAllOrder(); for (Order order1 : allOrder) { System.out.println(order1); /* Order{id=3, name=‘订单三‘, users=[User{id=2, username=‘shi‘, password=‘5678‘}, User{id=6, username=‘mark‘, password=‘1111‘}]} Order{id=4, name=‘订单三‘, users=[User{id=3, username=‘xiong‘, password=‘9012‘}, User{id=2, username=‘shi‘, password=‘5678‘}]} Order{id=5, name=‘订单三‘, users=[User{id=3, username=‘xiong‘, password=‘9012‘}]} Order{id=6, name=‘订单三‘, users=[User{id=3, username=‘xiong‘, password=‘9012‘}]} Order{id=1, name=‘订单一‘, users=[]} Order{id=2, name=‘订单二‘, users=[]} */ } sqlSession.close(); } @Test public void order() { // 建立sql连接会话 SqlSession sqlSession = MyBatisUtils.openSession(); // 获取订单映射 OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class); // 通过分步查询获取订单 Order order = orderMapper.getOrderById(2); System.out.println(order); // Order{id=3, name=‘订单三‘, users=[User{id=2, username=‘shi‘, password=‘5678‘}, User{id=6, username=‘mark‘, password=‘1111‘}]} System.out.println(order); sqlSession.close(); } }
源码地址都是前面提到的GitHub上,有兴趣的可以去看。