首先,新建项目:
在pom.xml中导入相关依赖:mybatis、mysql、Junit、log4j、
当我们使用web项目,需要访问http://localhost:8080/xx/xx,通过部署到Tomcat容器中,放在webapp中,需要通过classpath(这里的classpath就等于webapp)的db.properties访问。而我们现在是非web项目,只有resources,所以需要在pom.xml中写入
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml
**/ *.properties</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.xml
**/ *.properties</include>
</includes>
</resource>
</resources>
</build>
这里的**.*.xml
中的“**
”表示所有的目录,无论多少级目录,(最多1024级)都可以找到以".xml"结尾的文件。
在java包中新建mapper包(遵循7项规则)和po包:
仍是以最简单的select * from user为例。
mapper包中的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">
<mapper namespace="com.bit.mapper.UserMapper">
<resultMap id="userResultMap" type="com.bit.po.User">
<result column="username" property="userName"/>
<result column="id" property="userId"/>
<result column="birthday" property="birthday"/>
<result column="sex" property="userSex"/>
<result column="address" property="userAddress"/>
</resultMap>
<select id="queryUser" parameterType="int" resultMap="userResultMap">
select * from user where id=#{
value};
</select>
</mapper>
UserMapper接口如下所示:
package com.bit.mapper;
import com.bit.po.User;
import java.util.List;
public interface UserMapper {
public User queryUser(int myId)throw Exception;
}
在po包中写User类:(toString、getter、setter方法省略)
package com.bit.po;
import java.util.Date;
public class User
{
public User()
{
}
private int userId
private String userName;
private String userSex;
private Date birthday;
private String userAddress;
}
在resources中新建sqlMapConfig.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>
<properties resource="db.properties"/>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<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>
</environment>
</environments>
<mappers>
<package name="com.bit.mapper"></package>
</mappers>
</configuration>
因为要使用单元测试,需要日志打印信息,在resources中新增log4j.properties:
log4j.rootLogger=DEBUG, Console
#Mybatis log4j
#Console
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=%d [%t] %-5p [%c] - %m%n
log4j.logger.java.sql.ResultSet=INFO
log4j.logger.org.apache=INFO
log4j.logger.java.sql.Connection=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
进行单元测试:
(使用IO的时候一定要try catch)
import com.bit.mapper.UserMapper;
import com.bit.po.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Before;
import org.junit.Test;
import java.io.InputStream;
public class MybatisTest {
private SqlSessionFactory sqlSessionFactory;
@Before
public void testInit()
{
String file="sqlMapConfig.xml";
try{
InputStream inputStream=null;
inputStream=Resources.getResourceAsStream(file);
sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
}
catch (Exception e)
{
e.printStackTrace();}
}
@Test
public void testQueryUserById() throws Exception {
SqlSession sqlSession=sqlSessionFactory.openSession();
UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
User result=userMapper.queryUser(30);
System.out.println(result);
sqlSession.close();
}
}
运行结果为:User{userId=30, userName=‘Arvin’, userSex=‘2’, birthday=null, userAddress=‘null’}
缓存是这样的,我们是使用sqlSession执行操作的,当访问数据库时,通过sqlsession发出sql语句,当第二次使用sqlsession,想从数据库执行查询时,它先不发出sql语句,而是从sqlsession里面去查,看在sqlsession中有无相关缓存,如果没有,再去数据库中查,如果有,就不去数据库,就不发出sql语句了——这说明,sqlsession具有缓存数据的能力,在Mybatis框架中,sqlsession就是一级缓存。
一级缓存的特点:1.直接使用,无须配置。 2。不得不用,无法剔除。
3.无法管理一级缓存。
SqlSession sqlSession=sqlSessionFactory.openSession();
UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
//第一次查询,原则是:先到sqlsession中看有无需要的记录,如果没有,去DB中查,发出sql'语句
User result=userMapper.queryUser(30);
//当执行queryUser(int myid)方法时,就会写入一级缓存
//sqlsession就是一级缓存,就是个HashMap
System.out.println(result);
//第二次查询,先到sqlsession去看有无需要的记录,如果有,就不去DB中查,不发出sql语句,
// 直接从缓存中取数据
result=userMapper.queryUser(30);
System.out.println(result);
sqlSession.close();
当执行update、delete、insert操作时,就会清空一级缓存,之后就不能再从一级缓存中拿了,就会调用sql语句,去DB中查了。
在UserMapper.xml中写:
<update id="modifyUserById" parameterType="com.bit.po.User">
update user set username=#{
userName} where id = #{
userId}
</update>
接口中要有对应的方法:public void modifyUserById(User user) throws Exception;
在单元测试中写:
//清空一级缓存
result.setUserId(1);
result.setUserName("TTTTT");
result.setUserAddr("ttttt");
userMapper.modifyUserById(result);//发出update语句
sqlSession.commit();//清空一级缓存
而二级缓存是mapper级别的缓存,跨sqlsession的。每个mapper都有一个namespace,mapper之间可以共享缓存,二级缓存是需要配置的。分两步1.在sqlMapConfig.xml中配置总开关。2.在UserMapper中配置子开关。
二级缓存的特点:1.需要配置。2.可以管理(开启,关闭,清空,使用)
二级缓存的适用场景:1.经常被查询的数据
2.相对来说不重要的数据
3.对实时性要求不高的数据
4.在多表关联中,最好是存放单表的数据在缓存中
1.在sqlMapConfig.xml中配置总开关:
<settings>
<!--开启二级缓存总开关-->
<setting name="cacheEnabled" value="true"/>
</settings>
2.在UserMapper中配置子开关。
<!--该标签表示在某个mapper中开启二级缓存,子属性取默认值-->
<cache/>
Mybatis中的二级缓存没有指定一定要存在于内存当中,(如:可以存在于硬盘中)所以需要类实现序列化接口,为了能够被反序列化。因此po包的User类需要:public class User implements Serializable
对二级缓存进行单元测试:
//测试二级缓存
@Test
public void test2()
{
SqlSession sqlSession1=sqlSessionFactory.openSession();
SqlSession sqlSession2=sqlSessionFactory.openSession();
SqlSession sqlSession3=sqlSessionFactory.openSession();
try
{
UserMapper userMapper=sqlSession1.getMapper(UserMapper.class);
User user1=userMapper.queryUser(30);
System.out.println(user1);
//一级缓存关闭
sqlSession1.close();
UserMapper userMapper1=sqlSession2.getMapper(UserMapper.class);
User user2=userMapper1.queryUser(30);
System.out.println(user2);
}
catch (Exception ex){
ex.printStackTrace();}
finally {
sqlSession2.close();
}
}
既然把一级缓存关闭了,那么如果不发出sql语句,就说明是二级缓存在起作用。
同样的,二级缓存也是通过update,delete,insert来进行清空。sqlSession3就是用来清空缓存的。先把它(Id=30的元组)查询出来,再用它进行更新。
//通过update,delete,insert清空二级缓存
UserMapper userMapper2=sqlSession3.getMapper(UserMapper.class);
User user3=userMapper2.queryUser(30);
userMapper2.modifyUserById(user3);
sqlSession3.commit();
select orders.*,user.username,user.sex,user.address
from orders,user
where orders.user_id=user.id;
如果只是想查询某个订单号是哪个用户下的,需要使用延迟加载,先:
select * from orders;
查出订单,比如说看到了编号为“1000014”的订单,想知道这个订单是哪个用户下的,这时再写:
select username from user where orders.user_id=user,id and orders.number=1000014;
第二条语句是按需执行的。
我们把两条语句写成一条:
select orders.*,
(select username from user where orders.user_id = user.id)
as MyUserNanme,
(select sex from user where orders.user_id = user.id)
as MySex
from orders;
在命令行中执行结果如下:
先在mapper包下新建OrderMapper接口和OrderMapper.xml:
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">
<mapper namespace="bit.mapper.OrdersMapper">
<!--实现延迟加载-->
<resultMap id="orderuserlazyRM" type="com.bit.po.Orders">
<!--配置订单信息-->
<id column="id" property="id"/>
<result column="user_id" property="userid"/>
<result column="number" property="number"/>
<result column="createtime" property="createtime"/>
<result column="note" property="note"/>
<!--延迟加载用户信息-->
<!--select:指定延迟加载需要执行的statement的ID(根据user_id查询用户信息的statement)
该语句来自于UserMapper.xml文件中的queryUserById
column: 订单信息中关联用户信息的列名 user_id
关联SQL如下:
select orders.*,
(select username from user where orders.user_id = user.id) as MyUserNanme,
(select sex from user where orders.user_id = user.id) as MySex
from orders;
-->
!--订单到用户是1:1的关系-->
<association property="user" javaType="com.bit.po.User"
select="com.bit.mapper.UserMapper.queryUser" column="user_id"/>
</resultMap>
<!--实现延迟加载-->
<select id="findorderuserlazyload" resultMap="orderuserlazyRM">
select * from orders;
</select>
</mapper>
因为从订单到用户是一对一的关系,所以使用
接口中有相应方法:
package com.bit.mapper;
import com.bit.po.Orders;
import java.util.List;
public interface OrderMapper {
public List<Orders> findorderuserlazyload() throws Exception;
}
在po包下需要Orders类(省略getter、setter方法):
package com.bit.po;
import java.util.Date;
public class Orders {
public Orders()
{
}
private Integer id;
private Integer userid;
private String number;
private Date createtime;
private String note;
//1:1
private User user;
延时加载也是需要开关的,在sqlMapConfig.xml中写:
<!--延时加载的开关-->
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="true"/>
在单元测试中写:
//测试延迟加载
@Test
public void testLazyLoading()
{
SqlSession sqlSession=sqlSessionFactory.openSession();
try{
OrderMapper orderMapper=sqlSession.getMapper(com.bit.mapper.OrderMapper.class);
//此时发出sql语句:select * from orders
List<Orders> result=orderMapper.findorderuserlazyload();
//此时需求是根据orders查询user
//所以延迟发出第二条sql语句,即延迟加载
for(Orders orders:result)
{
System.out.println("username"+orders.getUser().getUserName());}
System.out.println(result);
}
catch (Exception ex){
ex.printStackTrace();}
finally {
sqlSession.close();
}
}
运行结果为:
延迟加载的适用场景:
1.当数据特别多的时候
2.当关联查询特别频繁的时候
3.当需要追求效率的时候
Mybatis的逆向工程
*是一个工具类,类似于我们使用的log4j
*反向帮我们生产po类,mapper接口,xxMapper.xml文件
步骤:
1.在pom.xml中添加逆向工程的plugin:
<plugins>
<!-- mybatis-generator自动生成代码插件 -->
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.5</version>
</plugin>
</plugins>
2.需要generatorConfig.xml:(见XiaoMing的mybatis_generator)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<!--
<classPathEntry location="D:\mvn_local_repository\mysql\mysql-connector-java\5.1.38\mysql-connector-java-5.1.38.jar" />
-->
<properties resource="db.properties"/>
<context id="mysql" targetRuntime="MyBatis3">
<!-- 生成的pojo,将implements Serializable-->
<plugin type="org.mybatis.generator.plugins.SerializablePlugin"/>
<!-- 是否去除自动生成的注释 true:是 : false:否 -->
<commentGenerator>
<property name="suppressAllComments" value="true"/>
</commentGenerator>
<!-- 数据库链接URL、用户名、密码 -->
<!-- <jdbcConnection
driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://127.0.0.1:3306/webtestdb"
userId="root"
password="root">
</jdbcConnection>-->
<jdbcConnection
driverClass="${jdbc.driver}"
connectionURL="${jdbc.url}"
userId="${jdbc.username}"
password="${jdbc.password}">
</jdbcConnection>
<!--
默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer
true,把JDBC DECIMAL 和 NUMERIC 类型解析为java.math.BigDecimal
-->
<javaTypeResolver>
<property name="forceBigDecimals" value="false"/>
</javaTypeResolver>
<!--
生成model模型,对应的包路径,以及文件存放路径(targetProject),targetProject可以指定具体的路径,如./src/main/java,
也可以使用“MAVEN”来自动生成,这样生成的代码会在target/generatord-source目录下
-->
<!--<javaModelGenerator targetPackage="com.joey.mybaties.test.pojo" targetProject="MAVEN">-->
<javaModelGenerator targetPackage="com.arvin.po" targetProject="./src/main/java">
<!-- 在targetPackage的基础上,根据数据库的schema再生成一层package,最终生成的类放在这个package下,默认为false -->
<property name="enableSubPackages" value="false"/>
<!-- 从数据库返回的值被清理前后的空格 -->
<property name="trimStrings" value="true"/>
</javaModelGenerator>
<!--对应的mapper.xml文件 -->
<sqlMapGenerator targetPackage="com.arvin.mapper" targetProject="./src/main/java">
<!-- 在targetPackage的基础上,根据数据库的schema再生成一层package,最终生成的类放在这个package下,默认为false -->
<property name="enableSubPackages" value="false"/>
</sqlMapGenerator>
<!-- 对应的Mapper接口类文件 -->
<javaClientGenerator targetPackage="com.arvin.mapper" type="XMLMAPPER" targetProject="./src/main/java">
<!-- 在targetPackage的基础上,根据数据库的schema再生成一层package,最终生成的类放在这个package下,默认为false -->
<property name="enableSubPackages" value="false"/>
</javaClientGenerator>
<!-- 列出要生成代码的所有表,这里配置的是不生成Example文件 -->
<!--指定数据库表-->
<table tableName="items" domainObjectName="Items"/>
<table tableName="orders" domainObjectName="Orders"/>
<table tableName="orderdetail" domainObjectName="Orderdetail"/>
<table tableName="user" domainObjectName="User"/>
</context>
</generatorConfiguration>
2.在Maven项目的resource文件目录中引
3.配置generatorConfig,xml文件
4.编写客户端调用generatorConfig.xml文件