public static void main(String[] args) { Connection connection = null; PreparedStatement preparedStatement = null; ResultSet resultSet = null; try { //加载数据库驱动 Class.forName("com.mysql.jdbc.Driver"); //经过驱动管理类获取数据库连接 connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8", "root", "mysql"); //定义sql语句 ?表示占位符 String sql = "select * from user where username = ?"; //获取预处理statement preparedStatement = connection.prepareStatement(sql); //设置参数,第一个参数为sql语句中参数的序号(从1开始),第二个参数为设置的参数值 preparedStatement.setString(1, "王五"); //向数据库发出sql执行查询,查询出结果集 resultSet = preparedStatement.executeQuery(); //遍历查询结果集 while(resultSet.next()){ System.out.println(resultSet.getString("id")+" "+resultSet.getString("username")); } } catch (Exception e) { e.printStackTrace(); }finally{ //释放资源 if(resultSet!=null){ try { resultSet.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if(preparedStatement!=null){ try { preparedStatement.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if(connection!=null){ try { connection.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
上边使用jdbc的原始方法(未经封装)实现了查询数据库表记录的操做。前端
MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,而且更名为MyBatis,实质上Mybatis对ibatis进行一些改进。java
MyBatis是一个优秀的持久层框架,它对jdbc的操做数据库的过程进行封装,使开发者只须要关注 SQL 自己,而不须要花费精力去处理例如注册驱动、建立connection、建立statement、手动设置参数、结果集检索等jdbc繁杂的过程代码。mysql
Mybatis经过xml或注解的方式将要执行的各类statement(statement、preparedStatemnt、CallableStatement)配置起来,并经过java对象和statement中的sql进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射成java对象并返回。git
SqlMapConfig.xml,此文件做为mybatis的全局配置文件,配置了mybatis的运行环境等信息。程序员
mapper.xml文件即sql映射文件,文件中配置了操做数据库的sql语句。此文件须要在SqlMapConfig.xml中加载。github
mybaits的代码由github.com管理,地址:https://github.com/mybatis/mybatis-3/releasesredis
mybatis-3.2.7.jar----mybatis的核心包spring
lib----mybatis的依赖包sql
mybatis-3.2.7.pdf----mybatis使用手册数据库
先导入sql_table.sql,再导入 sql_data.sql脚本:
以下:
实现如下功能:
根据用户id查询一个用户信息
根据用户名称模糊查询用户信息列表
添加用户
更新用户
删除用户
使用eclipse建立java工程,jdk使用1.7.0_72。
加入mybatis核心包、依赖包、数据驱动包。
在classpath下建立log4j.properties以下:
# Global logging configuration
log4j.rootLogger=DEBUG, stdout
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
mybatis默认使用log4j做为输出日志信息。
在classpath下建立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> <!-- 和spring整合后 environments配置将废除--> <environments default="development"> <environment id="development"> <!-- 使用jdbc事务管理--> <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="mysql" /> </dataSource> </environment> </environments> </configuration>
SqlMapConfig.xml是mybatis核心配置文件,上边文件的配置内容为数据源、事务管理。
Po类做为mybatis进行sql映射使用,po类一般与数据库表对应,User.java以下:
public class User { private int id; private String username;// 用户姓名 private String sex;// 性别 private Date birthday;// 生日 private String address;// 地址 get/set……
在classpath下的sqlmap目录下建立sql映射文件Users.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="test"> </mapper>
namespace :命名空间,用于隔离sql语句,后面会讲另外一层很是重要的做用。
在SqlMapConfig.xml中添加:
<!-- 根据id获取用户信息 --> <select id="findUserById" parameterType="int"resultType="cn.itcast.mybatis.po.User"> select * from user where id = #{id} </select> <!-- 自定义条件查询用户列表 --> <select id="findUserByUsername" parameterType="java.lang.String" resultType="cn.itcast.mybatis.po.User"> select * from user where username like '%${value}%' </select>
parameterType:定义输入到sql中的映射类型,#{id}表示使用preparedstatement设置占位符号并将输入变量id传到sql。
resultType:定义结果映射类型。
mybatis框架须要加载映射文件,将Users.xml添加在SqlMapConfig.xml,以下:
<mappers> <mapper resource="sqlmap/User.xml"/> </mappers>
public class Mybatis_first { //会话工厂 private SqlSessionFactory sqlSessionFactory; @Before public void createSqlSessionFactory() throws IOException { // 配置文件 String resource = "SqlMapConfig.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); // 使用SqlSessionFactoryBuilder从xml配置文件中建立SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder() .build(inputStream); } // 根据 id查询用户信息 @Test public void testFindUserById() { // 数据库会话实例 SqlSession sqlSession = null; try { // 建立数据库会话实例sqlSession sqlSession = sqlSessionFactory.openSession(); // 查询单个记录,根据用户id查询用户信息 User user = sqlSession.selectOne("test.findUserById", 10); // 输出用户信息 System.out.println(user); } catch (Exception e) { e.printStackTrace(); } finally { if (sqlSession != null) { sqlSession.close(); } } } // 根据用户名称模糊查询用户信息 @Test public void testFindUserByUsername() { // 数据库会话实例 SqlSession sqlSession = null; try { // 建立数据库会话实例sqlSession sqlSession = sqlSessionFactory.openSession(); // 查询单个记录,根据用户id查询用户信息 List<User> list = sqlSession.selectList("test.findUserByUsername", "张"); System.out.println(list.size()); } catch (Exception e) { e.printStackTrace(); } finally { if (sqlSession != null) { sqlSession.close(); } } } }
#{}表示一个占位符号,经过#{}能够实现preparedStatement向占位符中设置值,自动进行java类型和jdbc类型转换,#{}能够有效防止sql注入。 #{}能够接收简单类型值或pojo属性值。 若是parameterType传输单个简单类型值,#{}括号中能够是value或其它名称。
${}表示拼接sql串,经过${}能够将parameterType 传入的内容拼接在sql中且不进行jdbc类型转换, ${}能够接收简单类型值或pojo属性值,若是parameterType传输单个简单类型值,${}括号中只能是value。
parameterType:指定输入参数类型,mybatis经过ognl从输入对象中获取参数值拼接在sql中。
resultType:指定输出结果类型,mybatis将sql查询结果的一行记录数据映射为resultType指定类型的对象。
selectOne查询一条记录,若是使用selectOne查询多条记录则抛出异常:
org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 3
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectOne(DefaultSqlSession.java:70)
selectList能够查询一条或多条记录。
在SqlMapConfig.xml中添加:
<!-- 添加用户 --> <insert id="insertUser"parameterType="cn.itcast.mybatis.po.User"> <selectKey keyProperty="id" order="AFTER"resultType="java.lang.Integer"> select LAST_INSERT_ID() </selectKey> insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address}) </insert>
// 添加用户信息 @Test public void testInsert() { // 数据库会话实例 SqlSession sqlSession = null; try { // 建立数据库会话实例sqlSession sqlSession = sqlSessionFactory.openSession(); // 添加用户信息 User user = new User(); user.setUsername("张小明"); user.setAddress("河南郑州"); user.setSex("1"); user.setPrice(1999.9f); sqlSession.insert("test.insertUser", user); //提交事务 sqlSession.commit(); } catch (Exception e) { e.printStackTrace(); } finally { if (sqlSession != null) { sqlSession.close(); } } }
经过修改sql映射文件,能够将mysql自增主键返回:
<insert id="insertUser" parameterType="cn.itcast.mybatis.po.User"> <!-- selectKey将主键返回,须要再返回 --> <selectKey keyProperty="id" order="AFTER"resultType="java.lang.Integer"> select LAST_INSERT_ID() </selectKey> insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address}); </insert>
添加selectKey实现将主键返回
keyProperty:返回的主键存储在pojo中的哪一个属性
order:selectKey的执行顺序,是相对与insert语句来讲,因为mysql的自增原理执行完insert语句以后才将主键生成,因此这里selectKey的执行顺序为after
resultType:返回的主键是什么类型
LAST_INSERT_ID():是mysql的函数,返回auto_increment自增列新记录id值。
须要增长经过select uuid()获得uuid值
<insert id="insertUser" parameterType="cn.itcast.mybatis.po.User">
<selectKey resultType="java.lang.String" order="BEFORE"
keyProperty="id">
select uuid()
</selectKey>
insert into user(id,username,birthday,sex,address)
values(#{id},#{username},#{birthday},#{sex},#{address})
</insert>
注意这里使用的order是“BEFORE”
首先自定义一个序列且用于生成主键,selectKey使用以下:
<insert id="insertUser" parameterType="cn.itcast.mybatis.po.User">
<selectKey resultType="java.lang.Integer" order="BEFORE"
keyProperty="id">
SELECT 自定义序列.NEXTVAL FROM DUAL
</selectKey>
insert into user(id,username,birthday,sex,address)
values(#{id},#{username},#{birthday},#{sex},#{address})
</insert>
注意这里使用的order是“BEFORE”
<!-- 删除用户 --> <delete id="deleteUserById" parameterType="int"> delete from user where id=#{id} </delete>
// 根据id删除用户 @Test public void testDelete() { // 数据库会话实例 SqlSession sqlSession = null; try { // 建立数据库会话实例sqlSession sqlSession = sqlSessionFactory.openSession(); // 删除用户 sqlSession.delete("test.deleteUserById",18); // 提交事务 sqlSession.commit(); } catch (Exception e) { e.printStackTrace(); } finally { if (sqlSession != null) { sqlSession.close(); } } }
<!-- 更新用户 --> <update id="updateUser"parameterType="cn.itcast.mybatis.po.User"> update user set username=#{username},birthday=#{birthday},sex=#{sex},address=#{address} where id=#{id} </update>
// 更新用户信息 @Test public void testUpdate() { // 数据库会话实例 SqlSession sqlSession = null; try { // 建立数据库会话实例sqlSession sqlSession = sqlSessionFactory.openSession(); // 添加用户信息 User user = new User(); user.setId(16); user.setUsername("张小明"); user.setAddress("河南郑州"); user.setSex("1"); user.setPrice(1999.9f); sqlSession.update("test.updateUser", user); // 提交事务 sqlSession.commit(); } catch (Exception e) { e.printStackTrace(); } finally { if (sqlSession != null) { sqlSession.close(); } } }
解决:在SqlMapConfig.xml中配置数据连接池,使用链接池管理数据库连接。
解决:将Sql语句配置在XXXXmapper.xml文件中与java代码分离。
解决:Mybatis自动将java对象映射至sql语句,经过statement中的parameterType定义输入参数的类型。
解决:Mybatis自动将sql执行结果映射至java对象,经过statement中的resultType定义输出结果的类型。
Mybatis和hibernate不一样,它不彻底是一个ORM框架,由于MyBatis须要程序员本身编写Sql语句,不过mybatis能够经过XML或注解方式灵活配置要运行的sql语句,并将java对象和sql语句映射生成最终执行的sql,最后将sql执行的结果再映射生成java对象。
Mybatis学习门槛低,简单易学,程序员直接编写原生态sql,可严格控制sql执行性能,灵活度高,很是适合对关系数据模型要求不高的软件开发,例如互联网软件、企业运营类软件等,由于这类软件需求变化频繁,一但需求变化要求成果输出迅速。可是灵活的前提是mybatis没法作到数据库无关性,若是须要实现支持多种数据库的软件则须要自定义多套sql映射文件,工做量大。
Hibernate对象/关系映射能力强,数据库无关性好,对于关系模型要求高的软件(例如需求固定的定制化软件)若是用hibernate开发能够节省不少代码,提升效率。可是Hibernate的学习门槛高,要精通门槛更高,并且怎么设计O/R映射,在性能和对象模型之间如何权衡,以及怎样用好Hibernate须要具备很强的经验和能力才行。
总之,按照用户的需求在有限的资源环境下只要能作出维护性、扩展性良好的软件架构都是好架构,因此框架只有适合才是最好。
使用Mybatis开发Dao,一般有两个方法,即原始Dao开发方法和Mapper接口开发方法。
将下边的功能实现Dao:
根据用户id查询一个用户信息
根据用户名称模糊查询用户信息列表
添加用户信息
SqlSession中封装了对数据库的操做,如:查询、插入、更新、删除等。
经过SqlSessionFactory建立SqlSession,而SqlSessionFactory是经过SqlSessionFactoryBuilder进行建立。
SqlSessionFactoryBuilder用于建立SqlSessionFacoty,SqlSessionFacoty一旦建立完成就不须要SqlSessionFactoryBuilder了,由于SqlSession是经过SqlSessionFactory生产,因此能够将SqlSessionFactoryBuilder当成一个工具类使用,最佳使用范围是方法范围即方法体内局部变量。
SqlSessionFactory是一个接口,接口中定义了openSession的不一样重载方法,SqlSessionFactory的最佳使用范围是整个应用运行期间,一旦建立后能够重复使用,一般以单例模式管理SqlSessionFactory。
SqlSession是一个面向用户的接口, sqlSession中定义了数据库操做,默认使用DefaultSqlSession实现类。
执行过程以下:
Environment environment = configuration.getEnvironment();
if (ExecutorType.BATCH == executorType) { executor = newBatchExecutor(this, transaction); } elseif (ExecutorType.REUSE == executorType) { executor = new ReuseExecutor(this, transaction); } else { executor = new SimpleExecutor(this, transaction); } if (cacheEnabled) { executor = new CachingExecutor(executor, autoCommit); }
结论:
每一个线程都应该有它本身的SqlSession实例。SqlSession的实例不能共享使用,它也是线程不安全的。所以最佳的范围是请求或方法范围。绝对不能将SqlSession实例的引用放在一个类的静态字段或实例字段中。
打开一个 SqlSession;使用完毕就要关闭它。一般把这个关闭操做放到 finally块中以确保每次都能执行关闭。以下:
SqlSession session = sqlSessionFactory.openSession(); try { // do work } finally { session.close(); }
原始Dao开发方法须要程序员编写Dao接口和Dao实现类。
<?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="test"> <!-- 根据id获取用户信息 --> <select id="findUserById" parameterType="int"resultType="cn.itcast.mybatis.po.User"> select * from user where id = #{id} </select> <!-- 添加用户 --> <insert id="insertUser"parameterType="cn.itcast.mybatis.po.User"> <selectKey keyProperty="id" order="AFTER"resultType="java.lang.Integer"> select LAST_INSERT_ID() </selectKey> insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address}) </insert> </mapper>
Public interface UserDao { public User getUserById(int id) throws Exception; public void insertUser(User user) throws Exception; } Public class UserDaoImpl implements UserDao { //注入SqlSessionFactory public UserDaoImpl(SqlSessionFactory sqlSessionFactory){ this.setSqlSessionFactory(sqlSessionFactory); } private SqlSessionFactory sqlSessionFactory; @Override public User getUserById(int id) throws Exception { SqlSession session = sqlSessionFactory.openSession(); User user = null; try { //经过sqlsession调用selectOne方法获取一条结果集 //参数1:指定定义的statement的id,参数2:指定向statement中传递的参数 user = session.selectOne("test.findUserById", 1); System.out.println(user); } finally{ session.close(); } return user; } @Override Public void insertUser(User user) throws Exception { SqlSession sqlSession = sqlSessionFactory.openSession(); try { sqlSession.insert("insertUser", user); sqlSession.commit(); } finally{ session.close(); } } }
原始Dao开发中存在如下问题:
Mapper接口开发方法只须要程序员编写Mapper接口(至关于Dao接口),由Mybatis框架根据接口定义建立接口的动态代理对象,代理对象的方法体同上边Dao接口实现类方法。
Mapper接口开发须要遵循如下规范:
定义mapper映射文件UserMapper.xml(内容同Users.xml),须要修改namespace的值为 UserMapper接口路径。将UserMapper.xml放在classpath下mapper目录 下。
<?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="cn.itcast.mybatis.mapper.UserMapper"> <!-- 根据id获取用户信息 --> <select id="findUserById" parameterType="int"resultType="cn.itcast.mybatis.po.User"> select * from user where id = #{id} </select> <!-- 自定义条件查询用户列表 --> <select id="findUserByUsername" parameterType="java.lang.String" resultType="cn.itcast.mybatis.po.User"> select * from user where username like '%${value}%' </select> <!-- 添加用户 --> <insert id="insertUser"parameterType="cn.itcast.mybatis.po.User"> <selectKey keyProperty="id" order="AFTER"resultType="java.lang.Integer"> select LAST_INSERT_ID() </selectKey> insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address}) </insert> </mapper>
/** * 用户管理mapper */ Public interface UserMapper { //根据用户id查询用户信息 public User findUserById(int id) throws Exception; //查询用户列表 public List<User> findUserByUsername(String username) throwsException; //添加用户信息 public void insertUser(User user)throws Exception; }
接口定义有以下特色:
修改SqlMapConfig.xml文件:
<!-- 加载映射文件 --> <mappers> <mapper resource="mapper/UserMapper.xml"/> </mappers>
Public class UserMapperTest extends TestCase { private SqlSessionFactory sqlSessionFactory; protected void setUp() throws Exception { //mybatis配置文件 String resource = "sqlMapConfig.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); //使用SqlSessionFactoryBuilder建立sessionFactory sqlSessionFactory = newSqlSessionFactoryBuilder().build(inputStream); } Public void testFindUserById() throws Exception { //获取session SqlSession session = sqlSessionFactory.openSession(); //获取mapper接口的代理对象 UserMapper userMapper = session.getMapper(UserMapper.class); //调用代理对象方法 User user = userMapper.findUserById(1); System.out.println(user); //关闭session session.close(); } @Test public void testFindUserByUsername() throws Exception { SqlSession sqlSession = sqlSessionFactory.openSession(); UserMapper userMapper = sqlSession.getMapper(UserMapper.class); List<User> list = userMapper.findUserByUsername("张"); System.out.println(list.size()); } Public void testInsertUser() throws Exception { //获取session SqlSession session = sqlSessionFactory.openSession(); //获取mapper接口的代理对象 UserMapper userMapper = session.getMapper(UserMapper.class); //要添加的数据 User user = new User(); user.setUsername("张三"); user.setBirthday(new Date()); user.setSex("1"); user.setAddress("北京市"); //经过mapper接口添加用户 userMapper.insertUser(user); //提交 session.commit(); //关闭session session.close(); } }
动态代理对象调用sqlSession.selectOne()和sqlSession.selectList()是根据mapper接口方法的返回值决定,若是返回list则调用selectList方法,若是返回单个对象则调用selectOne方法。
mybatis官方推荐使用mapper代理方法开发mapper接口,程序员不用编写mapper接口实现类,使用mapper代理方法时,输入参数可使用pojo包装对象或map对象,保证dao的通用性。
SqlMapConfig.xml中配置的内容和顺序以下:
properties(属性)
settings(全局配置参数)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境集合属性对象)
environment(环境子属性对象)
transactionManager(事务管理)
dataSource(数据源)
mappers(映射器)
SqlMapConfig.xml能够引用java属性文件中的配置信息以下:
在classpath下定义db.properties文件,
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis
jdbc.username=root
jdbc.password=mysql
SqlMapConfig.xml引用以下:
<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>
注意: MyBatis 将按照下面的顺序来加载属性:
所以,经过parameterType传递的属性具备最高优先级,resource或 url 加载的属性次之,最低优先级的是 properties 元素体内定义的属性。
mybatis全局配置参数,全局参数将会影响mybatis的运行行为。
详细参见“学习资料/mybatis-settings.xlsx”文件
别名 |
映射的类型 |
_byte |
byte |
_long |
long |
_short |
short |
_int |
int |
_integer |
int |
_double |
double |
_float |
float |
_boolean |
boolean |
string |
String |
byte |
Byte |
long |
Long |
short |
Short |
int |
Integer |
integer |
Integer |
double |
Double |
float |
Float |
boolean |
Boolean |
date |
Date |
decimal |
BigDecimal |
bigdecimal |
BigDecimal |
在SqlMapConfig.xml中配置:
<typeAliases> <!-- 单个别名定义 --> <typeAlias alias="user" type="cn.itcast.mybatis.po.User"/> <!-- 批量别名定义,扫描整个包下的类,别名为类名(首字母大写或小写均可以) --> <package name="cn.itcast.mybatis.po"/> <package name="其它包"/> </typeAliases>
类型处理器用于java类型和jdbc类型映射,以下:
<select id="findUserById" parameterType="int" resultType="user"> select * from user where id = #{id} </select>
mybatis自带的类型处理器基本上知足平常需求,不须要单独定义。
mybatis支持类型处理器:
类型处理器 |
Java类型 |
JDBC类型 |
BooleanTypeHandler |
Boolean,boolean |
任何兼容的布尔值 |
ByteTypeHandler |
Byte,byte |
任何兼容的数字或字节类型 |
ShortTypeHandler |
Short,short |
任何兼容的数字或短整型 |
IntegerTypeHandler |
Integer,int |
任何兼容的数字和整型 |
LongTypeHandler |
Long,long |
任何兼容的数字或长整型 |
FloatTypeHandler |
Float,float |
任何兼容的数字或单精度浮点型 |
DoubleTypeHandler |
Double,double |
任何兼容的数字或双精度浮点型 |
BigDecimalTypeHandler |
BigDecimal |
任何兼容的数字或十进制小数类型 |
StringTypeHandler |
String |
CHAR和VARCHAR类型 |
ClobTypeHandler |
String |
CLOB和LONGVARCHAR类型 |
NStringTypeHandler |
String |
NVARCHAR和NCHAR类型 |
NClobTypeHandler |
String |
NCLOB类型 |
ByteArrayTypeHandler |
byte[] |
任何兼容的字节流类型 |
BlobTypeHandler |
byte[] |
BLOB和LONGVARBINARY类型 |
DateTypeHandler |
Date(java.util) |
TIMESTAMP类型 |
DateOnlyTypeHandler |
Date(java.util) |
DATE类型 |
TimeOnlyTypeHandler |
Date(java.util) |
TIME类型 |
SqlTimestampTypeHandler |
Timestamp(java.sql) |
TIMESTAMP类型 |
SqlDateTypeHandler |
Date(java.sql) |
DATE类型 |
SqlTimeTypeHandler |
Time(java.sql) |
TIME类型 |
ObjectTypeHandler |
任意 |
其余或未指定类型 |
EnumTypeHandler |
Enumeration类型 |
VARCHAR-任何兼容的字符串类型,做为代码存储(而不是索引)。 |
Mapper配置的几种方法:
使用相对于类路径的资源
如:<mapper resource="sqlmap/User.xml" />
使用彻底限定路径
如:<mapper url="file:///D:\workspace_spingmvc\mybatis_01\config\sqlmap\User.xml" />
使用mapper接口类路径
如:<mapper class="cn.itcast.mybatis.mapper.UserMapper"/>
注意:此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中。
注册指定包下的全部mapper接口
如:<package name="cn.itcast.mybatis.mapper"/>
注意:此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中。
Mapper.xml映射文件中定义了操做数据库的sql,每一个sql是一个statement,映射文件是mybatis的核心。
#{}实现的是向prepareStatement中的预处理语句中设置参数值,sql语句中#{}表示一个占位符即?。
<!-- 根据id查询用户信息 --> <select id="findUserById" parameterType="int" resultType="user"> select * from user where id = #{id} </select>
使用占位符#{}能够有效防止sql注入,在使用时不须要关心参数值的类型,mybatis会自动进行java类型和jdbc类型的转换。#{}能够接收简单类型值或pojo属性值,若是parameterType传输单个简单类型值,#{}括号中能够是value或其它名称。
${}和#{}不一样,经过${}能够将parameterType 传入的内容拼接在sql中且不进行jdbc类型转换, ${}能够接收简单类型值或pojo属性值,若是parameterType传输单个简单类型值,${}括号中只能是value。使用${}不能防止sql注入,可是有时用${}会很是方便,以下的例子:
<!-- 根据名称模糊查询用户信息 --> <select id="selectUserByName" parameterType="string"resultType="user"> select * from user where username like '%${value}%' </select>
若是本例子使用#{}则传入的字符串中必须有%号,而%是人为拼接在参数中,显然有点麻烦,若是采用${}在sql中拼接为%的方式则在调用mapper接口传递参数就方便不少。
//若是使用占位符号则必须人为在传参数中加%
List<User> list = userMapper.selectUserByName("%管理员%");
//若是使用${}原始符号则不用人为在参数中加%
List<User>list = userMapper.selectUserByName("管理员");
再好比order by排序,若是将列名经过参数传入sql,根据传的列名进行排序,应该写为:
ORDER BY ${columnName}
若是使用#{}将没法实现此功能。
参考上边的例子。
Mybatis使用ognl表达式解析对象字段的值,以下例子:
<!—传递pojo对象综合查询用户信息 --> <select id="findUserByUser" parameterType="user"resultType="user"> select * from user where id=#{id} and username like '%${username}%' </select>
上边红色标注的是user对象中的字段名称。
测试:
Public void testFindUserByUser()throws Exception{ //获取session SqlSession session = sqlSessionFactory.openSession(); //获限mapper接口实例 UserMapper userMapper = session.getMapper(UserMapper.class); //构造查询条件user对象 User user = new User(); user.setId(1); user.setUsername("管理员"); //传递user对象查询用户列表 List<User>list = userMapper.findUserByUser(user); //关闭session session.close(); }
异常测试:
Sql中字段名输入错误后测试,username输入dusername测试结果报错:
org.apache.ibatis.exceptions.PersistenceException:
### Error querying database. Cause: org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 'dusername' in 'class cn.itcast.mybatis.po.User'
### Cause: org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 'dusername' in 'class cn.itcast.mybatis.po.User'
开发中经过pojo传递查询条件 ,查询条件是综合的查询条件,不只包括用户查询条件还包括其它的查询条件(好比将用户购买商品信息也做为查询条件),这时可使用包装对象传递输入参数。
定义包装对象将查询条件(pojo)以类组合的方式包装起来。
public class QueryVo { private User user; //自定义用户扩展类 private UserCustom userCustom;
说明:mybatis底层经过ognl从pojo中获取属性值:#{user.username},user便是传入的包装对象的属性。queryVo是别名,即上边定义的包装对象类型。
Sql映射文件定义以下:
<!-- 传递hashmap综合查询用户信息 --> <select id="findUserByHashmap" parameterType="hashmap"resultType="user"> select * from user where id=#{id} and username like '%${username}%' </select>
上边红色标注的是hashmap的key。
测试:
Public void testFindUserByHashmap()throws Exception{ //获取session SqlSession session = sqlSessionFactory.openSession(); //获限mapper接口实例 UserMapper userMapper = session.getMapper(UserMapper.class); //构造查询条件Hashmap对象 HashMap<String, Object> map = new HashMap<String, Object>(); map.put("id", 1); map.put("username", "管理员"); //传递Hashmap对象查询用户列表 List<User>list = userMapper.findUserByHashmap(map); //关闭session session.close(); }
异常测试:
传递的map中的key和sql中解析的key不一致。
测试结果没有报错,只是经过key获取值为空。
参考getnow输出日期类型,看下边的例子输出整型:
Mapper.xml文件
<!-- 获取用户列表总数 --> <select id="findUserCount" parameterType="user"resultType="int"> select count(1) from user </select>
Mapper接口
public int findUserCount(User user) throws Exception;
调用:
总结:
输出简单类型必须查询出来的结果集有一条记录,最终将第一个字段的值转换为输出类型。
使用session的selectOne可查询单条记录。
Public void testFindUserCount() throws Exception{ //获取session SqlSession session = sqlSessionFactory.openSession(); //获取mapper接口实例 UserMapper userMapper = session.getMapper(UserMapper.class); User user = new User(); user.setUsername("管理员"); //传递Hashmap对象查询用户列表 int count = userMapper.findUserCount(user); //关闭session session.close(); }
参考findUserById的定义:
Mapper.xml
<!-- 根据id查询用户信息 -->
<select id="findUserById" parameterType="int" resultType="user">
select * from user where id = #{id}
</select>
Mapper接口:
public User findUserById(int id) throws Exception;
测试:
Public void testFindUserById() throws Exception {
//获取session
SqlSession session = sqlSessionFactory.openSession();
//获限mapper接口实例
UserMapper userMapper = session.getMapper(UserMapper.class);
//经过mapper接口调用statement
User user = userMapper.findUserById(1);
System.out.println(user);
//关闭session
session.close();
}
使用session调用selectOne查询单条记录。
参考selectUserByName的定义:
Mapper.xml
<!-- 根据名称模糊查询用户信息 -->
<select id="findUserByUsername" parameterType="string"resultType="user">
select * from user where username like '%${value}%'
</select>
Mapper接口:
public List<User> findUserByUsername(String username) throws Exception;
测试:
Public void testFindUserByUsername()throws Exception{
//获取session
SqlSession session = sqlSessionFactory.openSession();
//获限mapper接口实例
UserMapper userMapper = session.getMapper(UserMapper.class);
//若是使用占位符号则必须人为在传参数中加%
//List<User> list = userMapper.selectUserByName("%管理员%");
//若是使用${}原始符号则不用人为在参数中加%
List<User> list = userMapper.findUserByUsername("管理员");
//关闭session
session.close();
}
使用session的selectList方法获取pojo列表。
输出pojo对象和输出pojo列表在sql中定义的resultType是同样的。
返回单个pojo对象要保证sql查询出来的结果集为单条,内部使用session.selectOne方法调用,mapper接口使用pojo对象做为方法返回值。
返回pojo列表表示查询出来的结果集可能为多条,内部使用session.selectList方法,mapper接口使用List<pojo>对象做为方法返回值。
输出pojo对象能够改用hashmap输出类型,将输出的字段名称做为map的key,value为字段值。
resultType能够指定pojo将查询结果映射为pojo,但须要pojo的属性名和sql查询的列名一致方可映射成功。
若是sql查询字段名和pojo的属性名不一致,能够经过resultMap将字段名和属性名做一个对应关系 ,resultMap实质上还须要将查询结果映射到pojo对象中。
resultMap能够实现将查询结果映射为复杂类型的pojo,好比在查询结果映射对象中包括pojo和list实现一对一查询和一对多查询。
使用resultMap指定上边定义的personmap。
因为上边的mapper.xml中sql查询列和Users.java类属性不一致,须要定义resultMap:userListResultMap将sql查询列和Users.java类属性对应起来
<id />:此属性表示查询结果集的惟一标识,很是重要。若是是多个字段为复合惟一约束则定义多个<id />。
Property:表示person类的属性。
Column:表示sql查询出来的字段名。
Column和property放在一起表示将sql查询出来的字段映射到指定的pojo类属性上。
<result />:普通结果,即pojo的属性。
public List<User> findUserListResultMap() throws Exception;
经过mybatis提供的各类标签方法实现动态拼接sql。
<!-- 传递pojo综合查询用户信息 -->
<select id="findUserList" parameterType="user"resultType="user">
select * from user
where 1=1
<if test="id!=null and id!=''">
and id=#{id}
</if>
<if test="username!=null and username!=''">
and username like '%${username}%'
</if>
</select>
注意要作不等于空字符串校验。
上边的sql也能够改成:
<select id="findUserList" parameterType="user" resultType="user">
select * from user
<where>
<if test="id!=null and id!=''">
and id=#{id}
</if>
<if test="username!=null and username!=''">
and username like '%${username}%'
</if>
</where>
</select>
<where />能够自动处理第一个and。
向sql传递数组或List,mybatis使用foreach解析,以下:
传入多个id查询用户信息,用下边两个sql实现:
SELECT * FROM USERS WHERE username LIKE '%张%' AND (id =10 OR id =89 OR id=16)
SELECT * FROM USERS WHERE username LIKE '%张%' id IN (10,89,16)
<if test="ids!=null and ids.size>0">
<foreach collection="ids" open=" and id in(" close=")"item="id" separator="," >
#{id}
</foreach>
</if>
List<Integer> ids = new ArrayList<Integer>();
ids.add(1);//查询id为1的用户
ids.add(10); //查询id为10的用户
queryVo.setIds(ids);
List<User> list = userMapper.findUserList(queryVo);
传递List类型在编写mapper.xml没有区别,惟一不一样的是只有一个List参数时它的参数名为list。
以下:
<select id="selectUserByList" parameterType="java.util.List"resultType="user">
select * from user
<where>
<!-- 传递List,List中是pojo -->
<if test="list!=null">
<foreach collection="list" item="item" open="and id in("separator=","close=")">
#{item.id}
</foreach>
</if>
</where>
</select>
public List<User> selectUserByList(List userlist) throws Exception;
Public void testselectUserByList()throws Exception{
//获取session
SqlSession session = sqlSessionFactory.openSession();
//获限mapper接口实例
UserMapper userMapper = session.getMapper(UserMapper.class);
//构造查询条件List
List<User> userlist = new ArrayList<User>();
User user = new User();
user.setId(1);
userlist.add(user);
user = new User();
user.setId(2);
userlist.add(user);
//传递userlist列表查询用户列表
List<User>list = userMapper.selectUserByList(userlist);
//关闭session
session.close();
}
请阅读文档学习。
<!-- 传递数组综合查询用户信息 -->
<select id="selectUserByArray" parameterType="Object[]"resultType="user">
select * from user
<where>
<!-- 传递数组 -->
<if test="array!=null">
<foreach collection="array" index="index" item="item"open="and id in("separator=","close=")">
#{item.id}
</foreach>
</if>
</where>
</select>
sql只接收一个数组参数,这时sql解析参数的名称mybatis固定为array,若是数组是经过一个pojo传递到sql则参数的名称为pojo中的属性名。
index:为数组的下标。
item:为数组每一个元素的名称,名称随意定义
open:循环开始
close:循环结束
separator:中间分隔输出
public List<User> selectUserByArray(Object[] userlist) throws Exception;
Public void testselectUserByArray()throws Exception{
//获取session
SqlSession session = sqlSessionFactory.openSession();
//获限mapper接口实例
UserMapper userMapper = session.getMapper(UserMapper.class);
//构造查询条件List
Object[] userlist = new Object[2];
User user = new User();
user.setId(1);
userlist[0]=user;
user = new User();
user.setId(2);
userlist[1]=user;
//传递user对象查询用户列表
List<User>list = userMapper.selectUserByArray(userlist);
//关闭session
session.close();
}
请阅读文档学习。
<!-- 传递数组综合查询用户信息 -->
<select id="selectUserByArray" parameterType="Object[]"resultType="user">
select * from user
<where>
<!-- 传递数组 -->
<if test="array!=null">
<foreach collection="array"index="index"item="item"open="and id in("separator=","close=")">
#{item}
</foreach>
</if>
</where>
</select>
若是数组中是简单类型则写为#{item},不用再经过ognl获取对象属性值了。
public List<User> selectUserByArray(Object[] userlist) throws Exception;
Public void testselectUserByArray()throws Exception{
//获取session
SqlSession session = sqlSessionFactory.openSession();
//获限mapper接口实例
UserMapper userMapper = session.getMapper(UserMapper.class);
//构造查询条件List
Object[] userlist = new Object[2];
userlist[0]=”1”;
userlist[1]=”2”;
//传递user对象查询用户列表
List<User>list = userMapper.selectUserByArray(userlist);
//关闭session
session.close();
}
Sql中可将重复的sql提取出来,使用时用include引用便可,最终达到sql重用的目的,以下:
<!-- 传递pojo综合查询用户信息 -->
<select id="findUserList" parameterType="user"resultType="user">
select * from user
<where>
<if test="id!=null and id!=''">
and id=#{id}
</if>
<if test="username!=null and username!=''">
and username like '%${username}%'
</if>
</where>
</select>
<sql id="query_user_where">
<if test="id!=null and id!=''">
and id=#{id}
</if>
<if test="username!=null and username!=''">
and username like '%${username}%'
</if>
</sql>
<select id="findUserList" parameterType="user" resultType="user">
select * from user
<where>
<include refid="query_user_where"/>
</where>
</select>
注意:若是引用其它mapper.xml的sql片断,则在引用时须要加上namespace,以下:
<include refid="namespace.sql片断”/>
案例:查询全部订单信息,关联查询下单用户信息。
注意:由于一个订单信息只会是一我的下的订单,因此从查询订单信息出发关联查询用户信息为一对一查询。若是从用户信息出发查询用户下的订单信息则为一对多查询,由于一个用户能够下多个订单。
使用resultType,定义订单信息po类,此po类中包括了订单信息和用户信息:
SELECT
orders.*,
user.username,
userss.address
FROM
orders,
user
WHERE orders.user_id = user.id
Po类中应该包括上边sql查询出来的全部字段,以下:
public class OrdersCustom extends Orders {
private String username;// 用户名称
private String address;// 用户地址
get/set。。。。
OrdersCustom类继承Orders类后OrdersCustom类包括了Orders类的全部字段,只须要定义用户的信息字段便可。
<!-- 查询全部订单信息 -->
<select id="findOrdersList"resultType="cn.itcast.mybatis.po.OrdersCustom">
SELECT
orders.*,
user.username,
user.address
FROM
orders, user
WHERE orders.user_id = user.id
</select>
public List<OrdersCustom> findOrdersList() throws Exception;
Public void testfindOrdersList()throws Exception{
//获取session
SqlSession session = sqlSessionFactory.openSession();
//获限mapper接口实例
UserMapper userMapper = session.getMapper(UserMapper.class);
//查询订单信息
List<OrdersCustom> list = userMapper.findOrdersList();
System.out.println(list);
//关闭session
session.close();
}
定义专门的po类做为输出类型,其中定义了sql查询结果集全部的字段。此方法较为简单,企业中使用广泛。
使用resultMap,定义专门的resultMap用于映射一对一查询结果。
SELECT
orders.*,
user.username,
user.address
FROM
orders,
user
WHERE orders.user_id = user.id
在Orders类中加入User属性,user属性中用于存储关联查询的用户信息,由于订单关联查询用户是一对一关系,因此这里使用单个User对象存储关联查询的用户信息。
<select id="findOrdersListResultMap" resultMap="userordermap">
SELECT
orders.*,
user.username,
user.address
FROM
orders, user
WHERE orders.user_id = user.id
</select>
这里resultMap指定userordermap。
须要关联查询映射的是用户信息,使用association将用户信息映射到订单对象的用户属性中。
<!-- 订单信息resultmap -->
<resultMap type="cn.itcast.mybatis.po.Orders" id="userordermap">
<!-- 这里的id,是mybatis在进行一对一查询时将user字段映射为user对象时要使用,必须写 -->
<id property="id" column="id"/>
<result property="user_id" column="user_id"/>
<result property="number" column="number"/>
<association property="user" javaType="cn.itcast.mybatis.po.User">
<!-- 这里的id为user的id,若是写上表示给user的id属性赋值 -->
<id property="id" column="user_id"/>
<result property="username" column="username"/>
<result property="address" column="address"/>
</association>
</resultMap>
association:表示进行关联查询单条记录
property:表示关联查询的结果存储在cn.itcast.mybatis.po.Orders的user属性中
javaType:表示关联查询的结果类型
<id property="id" column="user_id"/>:查询结果的user_id列对应关联对象的id属性,这里是<id />表示user_id是关联查询对象的惟一标识。
<result property="username" column="username"/>:查询结果的username列对应关联对象的username属性。
public List<Orders> findOrdersListResultMap() throws Exception;
Public void testfindOrdersListResultMap()throws Exception{
//获取session
SqlSession session = sqlSessionFactory.openSession();
//获限mapper接口实例
UserMapper userMapper = session.getMapper(UserMapper.class);
//查询订单信息
List<Orders> list = userMapper.findOrdersList2();
System.out.println(list);
//关闭session
session.close();
}
使用association完成关联查询,将关联查询信息映射到pojo对象中。
案例:查询全部订单信息及订单下的订单明细信息。
订单信息与订单明细为一对多关系。
使用resultMap实现以下:
SELECT
orders.*,
user.username,
user.address,
orderdetail.id orderdetail_id,
orderdetail.items_id,
orderdetail.items_num
FROM
orders,user,orderdetail
WHERE orders.user_id = user.id
AND orders.id = orderdetail.orders_id
在Orders类中加入User属性。
在Orders类中加入List<Orderdetail> orderdetails属性
<select id="findOrdersDetailList" resultMap="userorderdetailmap">
SELECT
orders.*,
user.username,
user.address,
orderdetail.id orderdetail_id,
orderdetail.items_id,
orderdetail.items_num
FROM orders,user,orderdetail
WHERE orders.user_id = user.id
AND orders.id = orderdetail.orders_id
</select>
<!-- 订单信息resultmap -->
<resultMap type="cn.itcast.mybatis.po.Orders"id="userorderdetailmap">
<id property="id"column="id"/>
<result property="user_id" column="user_id"/>
<result property="number" column="number"/>
<association property="user" javaType="cn.itcast.mybatis.po.User">
<id property="id" column="user_id"/>
<result property="username" column="username"/>
<result property="address" column="address"/>
</association>
<collection property="orderdetails"ofType="cn.itcast.mybatis.po.Orderdetail">
<id property="id" column="orderdetail_id"/>
<result property="items_id" column="items_id"/>
<result property="items_num" column="items_num"/>
</collection>
</resultMap>
黄色部分和上边一对一查询订单及用户信息定义的resultMap相同,
collection部分定义了查询订单明细信息。
collection:表示关联查询结果集
property="orderdetails":关联查询的结果集存储在cn.itcast.mybatis.po.Orders上哪一个属性。
ofType="cn.itcast.mybatis.po.Orderdetail":指定关联查询的结果集中的对象类型即List中的对象类型。
<id />及<result/>的意义同一对一查询。
上边定义的resultMap中黄色部分和一对一查询订单信息的resultMap相同,这里使用继承能够再也不填写重复的内容,以下:
<resultMap type="cn.itcast.mybatis.po.Orders"id="userorderdetailmap" extends="userordermap">
<collection property="orderdetails"ofType="cn.itcast.mybatis.po.Orderdetail">
<id property="id" column="orderdetail_id"/>
<result property="items_id" column="items_id"/>
<result property="items_num" column="items_num"/>
</collection>
</resultMap>
使用extends继承订单信息userordermap。
public List<Orders>findOrdersDetailList () throws Exception;
Public void testfindOrdersDetailList()throws Exception{
//获取session
SqlSession session = sqlSessionFactory.openSession();
//获限mapper接口实例
UserMapper userMapper = session.getMapper(UserMapper.class);
//查询订单信息
List<Orders> list = userMapper.findOrdersDetailList();
System.out.println(list);
//关闭session
session.close();
}
使用collection完成关联查询,将关联查询信息映射到集合对象。
查询用户购买的商品信息。
须要查询全部用户信息,关联查询订单及订单明细信息,订单明细信息中关联查询商品信息
SELECT
orders.*,
USER .username,
USER .address,
orderdetail.id orderdetail_id,
orderdetail.items_id,
orderdetail.items_num,
items.name items_name,
items.detail items_detail
FROM
orders,
USER,
orderdetail,
items
WHERE
orders.user_id = USER .id
AND orders.id = orderdetail.orders_id
AND orderdetail.items_id = items.id
在User中添加List<Orders> orders 属性,在Orders类中加入List<Orderdetail> orderdetails属性
须要关联查询映射的信息是:订单、订单明细、商品信息
订单:一个用户对应多个订单,使用collection映射到用户对象的订单列表属性中
订单明细:一个订单对应多个明细,使用collection映射到订单对象中的明细属性中
商品信息:一个订单明细对应一个商品,使用association映射到订单明细对象的商品属性中。
<!-- 一对多查询
查询用户信息、关联查询订单、订单明细信息、商品信息
-->
<resultMap type="cn.itcast.mybatis.po.User"id="userOrderListResultMap">
<id column="user_id" property="id"/>
<result column="username" property="username"/>
<collection property="orders"ofType="cn.itcast.mybatis.po.Orders">
<id column="id" property="id"/>
<result property="number" column="number"/>
<collection property="orderdetails"ofType="cn.itcast.mybatis.po.Orderdetail">
<id column="orderdetail_id" property="id"/>
<result property="ordersId" column="id"/>
<result property="itemsId" column="items_id"/>
<result property="itemsNum" column="items_num"/>
<association property="items"javaType="cn.itcast.mybatis.po.Items">
<id column="items_id" property="id"/>
<result column="items_name" property="name"/>
<result column="items_detail" property="detail"/>
</association>
</collection>
</collection>
</resultMap>
一对可能是多对多的特例,以下需求:
查询用户购买的商品信息,用户和商品的关系是多对多关系。
需求1:
查询字段:用户帐号、用户名称、用户性别、商品名称、商品价格(最多见)
企业开发中常见明细列表,用户购买商品明细列表,
使用resultType将上边查询列映射到pojo输出。
需求2:
查询字段:用户帐号、用户名称、购买商品数量、商品明细(鼠标移上显示明细)
使用resultMap将用户购买的商品明细列表映射到user对象中。
resultType:
做用:
将查询结果按照sql列名pojo属性名一致性映射到pojo中。
场合:
常见一些明细记录的展现,好比用户购买商品明细,将关联查询信息所有展现在页面时,此时可直接使用resultType将每一条记录映射到pojo中,在前端页面遍历list(list中是pojo)便可。
resultMap:
使用association和collection完成一对一和一对多高级映射(对结果有特殊的映射要求)。
association:
做用:
将关联查询信息映射到一个pojo对象中。
场合:
为了方便查询关联信息可使用association将关联订单信息映射为用户对象的pojo属性中,好比:查询订单及关联用户信息。
使用resultType没法将查询结果映射到pojo对象的pojo属性中,根据对结果集查询遍历的须要选择使用resultType仍是resultMap。
collection:
做用:
将关联查询信息映射到一个list集合中。
场合:
为了方便查询遍历关联信息可使用collection将关联信息映射到list集合中,好比:查询用户权限范围模块及模块下的菜单,可以使用collection将模块映射到模块list中,将菜单列表映射到模块对象的菜单list属性中,这样的做的目的也是方便对查询结果集进行遍历查询。
若是使用resultType没法将查询结果映射到list集合中。
须要查询关联信息时,使用mybatis延迟加载特性可有效的减小数据库压力,首次查询只查询主要信息,关联信息等用户获取时再加载。
在mybatis核心配置文件中配置:
lazyLoadingEnabled、aggressiveLazyLoading
设置项 |
描述 |
容许值 |
默认值 |
lazyLoadingEnabled |
全局性设置懒加载。若是设为‘false’,则全部相关联的都会被初始化加载。 |
true | false |
false |
aggressiveLazyLoading |
当设置为‘true’的时候,懒加载的对象可能被任何懒属性所有加载。不然,每一个属性都按需加载。 |
true | false |
true |
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
查询订单信息,关联查询用户信息。
默认只查询订单信息,当须要查询用户信息时再去查询用户信息。
SELECT
orders.*
FROM
orders
在Orders类中加入User属性。
<select id="findOrdersList3" resultMap="userordermap2">
SELECT
orders.*
FROM
orders
</select>
<!-- 订单信息resultmap -->
<resultMap type="cn.itcast.mybatis.po.Orders" id="userordermap2">
<id property="id" column="id"/>
<result property="user_id" column="user_id"/>
<result property="number" column="number"/>
<association property="user" javaType="cn.itcast.mybatis.po.User"select="findUserById" column="user_id"/>
</resultMap>
association:
select="findUserById":指定关联查询sql为findUserById
column="user_id":关联查询时将users_id列的值传入findUserById
最后将关联查询结果映射至cn.itcast.mybatis.po.User。
public List<Orders> findOrdersList3() throws Exception;
Public void testfindOrdersList3()throws Exception{
//获取session
SqlSession session = sqlSessionFactory.openSession();
//获限mapper接口实例
UserMapper userMapper = session.getMapper(UserMapper.class);
//查询订单信息
List<Orders> list = userMapper.findOrdersList3();
System.out.println(list);
//开始加载,经过orders.getUser方法进行加载
for(Orders orders:list){
System.out.println(orders.getUser());
}
//关闭session
session.close();
}
不使用mybatis提供的延迟加载功能是否能够实现延迟加载?
实现方法:
针对订单和用户两个表定义两个mapper方法。
一、订单查询mapper方法
二、根据用户id查询用户信息mapper方法
默认使用订单查询mapper方法只查询订单信息。
当须要关联查询用户信息时再调用根据用户id查询用户信息mapper方法查询用户信息。
一对多延迟加载的方法同一对一延迟加载,在collection标签中配置select内容。
本部份内容自学。
做用:
当须要查询关联信息时再去数据库查询,默认不去关联查询,提升数据库性能。
只有使用resultMap支持延迟加载设置。
场合:
当只有部分记录须要关联查询其它信息时,此时可按需延迟加载,须要关联查询时再向数据库发出sql,以提升数据库性能。
当所有须要关联查询信息时,此时不用延迟加载,直接将关联查询信息所有返回便可,可以使用resultType或resultMap完成映射。
以下图,是mybatis一级缓存和二级缓存的区别图解:
Mybatis一级缓存的做用域是同一个SqlSession,在同一个sqlSession中两次执行相同的sql语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据将再也不从数据库查询,从而提升查询效率。当一个sqlSession结束后该sqlSession中的一级缓存也就不存在了。Mybatis默认开启一级缓存。
Mybatis二级缓存是多个SqlSession共享的,其做用域是mapper的同一个namespace,不一样的sqlSession两次执行相同namespace下的sql语句且向sql中传递参数也相同即最终执行相同的sql语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据将再也不从数据库查询,从而提升查询效率。Mybatis默认没有开启二级缓存须要在setting全局参数中配置开启二级缓存。
下图是根据id查询用户的一级缓存图解:
一级缓存区域是根据SqlSession为单位划分的。
每次查询会先从缓存区域找,若是找不到从数据库查询,查询到数据将数据写入缓存。
Mybatis内部存储缓存使用一个HashMap,key为hashCode+sqlId+Sql语句。value为从查询出来映射生成的java对象
sqlSession执行insert、update、delete等操做commit提交后会清空缓存区域。
//获取session
SqlSession session = sqlSessionFactory.openSession();
//获限mapper接口实例
UserMapper userMapper = session.getMapper(UserMapper.class);
//第一次查询
User user1 = userMapper.findUserById(1);
System.out.println(user1);
//第二次查询,因为是同一个session则再也不向数据发出语句直接从缓存取出
User user2 = userMapper.findUserById(1);
System.out.println(user2);
//关闭session
session.close();
//获取session
SqlSession session = sqlSessionFactory.openSession();
//获限mapper接口实例
UserMapper userMapper = session.getMapper(UserMapper.class);
//第一次查询
User user1 = userMapper.findUserById(1);
System.out.println(user1);
//在同一个session执行更新
User user_update = new User();
user_update.setId(1);
user_update.setUsername("李奎");
userMapper.updateUser(user_update);
session.commit();
//第二次查询,虽然是同一个session可是因为执行了更新操做session的缓存被清空,这里从新发出sql操做
User user2 = userMapper.findUserById(1);
System.out.println(user2);
下图是多个sqlSession请求UserMapper的二级缓存图解。
二级缓存区域是根据mapper的namespace划分的,相同namespace的mapper查询数据放在同一个区域,若是使用mapper代理方法每一个mapper的namespace都不一样,此时能够理解为二级缓存区域是根据mapper划分。
每次查询会先从缓存区域找,若是找不到从数据库查询,查询到数据将数据写入缓存。
Mybatis内部存储缓存使用一个HashMap,key为hashCode+sqlId+Sql语句。value为从查询出来映射生成的java对象
sqlSession执行insert、update、delete等操做commit提交后会清空缓存区域。
在核心配置文件SqlMapConfig.xml中加入
<setting name="cacheEnabled" value="true"/>
|
描述 |
容许值 |
默认值 |
cacheEnabled |
对在此配置文件下的全部cache 进行全局性开/关设置。 |
true false |
true |
要在你的Mapper映射文件中添加一行: <cache /> ,表示此mapper开启二级缓存。
二级缓存须要查询结果映射的pojo对象实现java.io.Serializable接口实现序列化和反序列化操做,注意若是存在父类、成员pojo都须要实现序列化接口。
public class Orders implements Serializable
public class User implements Serializable
....
//获取session1
SqlSession session1 = sqlSessionFactory.openSession();
UserMapper userMapper = session1.getMapper(UserMapper.class);
//使用session1执行第一次查询
User user1 = userMapper.findUserById(1);
System.out.println(user1);
//关闭session1
session1.close();
//获取session2
SqlSession session2 = sqlSessionFactory.openSession();
UserMapper userMapper2 = session2.getMapper(UserMapper.class);
//使用session2执行第二次查询,因为开启了二级缓存这里从缓存中获取数据再也不向数据库发出sql
User user2 = userMapper2.findUserById(1);
System.out.println(user2);
//关闭session2
session2.close();
在statement中设置useCache=false能够禁用当前select语句的二级缓存,即每次查询都会发出sql去查询,默认状况是true,即该sql使用二级缓存。
<select id="findOrderListResultMap" resultMap="ordersUserMap"useCache="false">
在mapper的同一个namespace中,若是有其它insert、update、delete操做数据后须要刷新缓存,若是不执行刷新缓存会出现脏读。
设置statement配置中的flushCache="true" 属性,默认状况下为true即刷新缓存,若是改为false则不会刷新。使用缓存时若是手动修改数据库表中的查询数据会出现脏读。
以下:
<insert id="insertUser" parameterType="cn.itcast.mybatis.po.User"flushCache="true">
flushInterval(刷新间隔)能够被设置为任意的正整数,并且它们表明一个合理的毫秒形式的时间段。默认状况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新。
size(引用数目)能够被设置为任意正整数,要记住你缓存的对象数目和你运行环境的可用内存资源数目。默认值是1024。
readOnly(只读)属性能够被设置为true或false。只读的缓存会给全部调用者返回缓存对象的相同实例。所以这些对象不能被修改。这提供了很重要的性能优点。可读写的缓存会返回缓存对象的拷贝(经过序列化)。这会慢一些,可是安全,所以默认是false。
以下例子:
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
这个更高级的配置建立了一个 FIFO 缓存,并每隔 60 秒刷新,存数结果对象或列表的 512 个引用,并且返回的对象被认为是只读的,所以在不一样线程中的调用者之间修改它们会致使冲突。可用的收回策略有, 默认的是 LRU:
1. LRU – 最近最少使用的:移除最长时间不被使用的对象。
2. FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
3. SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
4. WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
EhCache 是一个纯Java的进程内缓存框架,是一种普遍使用的开源Java分布式缓存,具备快速、精干等特色,是Hibernate中默认的CacheProvider。
mybatis提供二级缓存Cache接口,以下:
它的默认实现类:
经过实现Cache接口能够实现mybatis缓存数据经过其它缓存数据库整合,mybatis的特长是sql操做,缓存数据的管理不是mybatis的特长,为了提升缓存的性能将mybatis和第三方的缓存数据库整合,好比ehcache、memcache、redis等。
maven坐标:
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.0.2</version>
</dependency>
classpath下添加:ehcache.xml
内容以下:
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
<diskStore path="F:\develop\ehcache" />
<defaultCache
maxElementsInMemory="1000"
maxElementsOnDisk="10000000"
eternal="false"
overflowToDisk="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
</defaultCache>
</ehcache>
属性说明:
diskStore:指定数据在磁盘中的存储位置。
defaultCache:当借助CacheManager.add("demoCache")建立Cache时,EhCache便会采用<defalutCache/>指定的的管理策略
如下属性是必须的:
maxElementsInMemory - 在内存中缓存的element的最大数目
maxElementsOnDisk - 在磁盘上缓存的element的最大数目,如果0表示无穷大
eternal - 设定缓存的elements是否永远不过时。若是为true,则缓存的数据始终有效,若是为false那么还要根据timeToIdleSeconds,timeToLiveSeconds判断
overflowToDisk - 设定当内存缓存溢出的时候是否将过时的element缓存到磁盘上
如下属性是可选的:
timeToIdleSeconds - 当缓存在EhCache中的数据先后两次访问的时间超过timeToIdleSeconds的属性取值时,这些数据便会删除,默认值是0,也就是可闲置时间无穷大
timeToLiveSeconds - 缓存element的有效生命期,默认是0.,也就是element存活时间无穷大
diskSpoolBufferSizeMB 这个参数设置DiskStore(磁盘缓存)的缓存区大小.默认是30MB.每一个Cache都应该有本身的一个缓冲区.
diskPersistent - 在VM重启的时候是否启用磁盘保存EhCache中的数据,默认是false。
diskExpiryThreadIntervalSeconds - 磁盘缓存的清理线程运行间隔,默认是120秒。每一个120s,相应的线程会进行一次EhCache中数据的清理工做
memoryStoreEvictionPolicy - 当内存缓存达到最大,有新的element加入的时候,移除缓存中element的策略。默认是LRU(最近最少使用),可选的有LFU(最不常使用)和FIFO(先进先出)
EhcacheCache是ehcache对Cache接口的实现:
修改mapper.xml文件,在cache中指定EhcacheCache。
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
根据需求调整缓存参数:
<cache type="org.mybatis.caches.ehcache.EhcacheCache" >
<property name="timeToIdleSeconds" value="3600"/>
<property name="timeToLiveSeconds" value="3600"/>
<!-- 同ehcache参数maxElementsInMemory -->
<property name="maxEntriesLocalHeap" value="1000"/>
<!-- 同ehcache参数maxElementsOnDisk -->
<property name="maxEntriesLocalDisk" value="10000000"/>
<property name="memoryStoreEvictionPolicy" value="LRU"/>
</cache>
对于访问多的查询请求且用户对查询结果实时性要求不高,此时可采用mybatis二级缓存技术下降数据库访问量,提升访问速度,业务场景好比:耗时较高的统计分析sql、电话帐单查询sql等。
实现方法以下:经过设置刷新间隔时间,由mybatis每隔一段时间自动清空缓存,根据数据变化频率设置缓存刷新间隔flushInterval,好比设置为30分钟、60分钟、24小时等,根据需求而定。
mybatis二级缓存对细粒度的数据级别的缓存实现很差,好比以下需求:对商品信息进行缓存,因为商品信息查询访问量大,可是要求用户每次都能查询最新的商品信息,此时若是使用mybatis的二级缓存就没法实现当一个商品变化时只刷新该商品的缓存信息而不刷新其它商品的信息,由于mybaits的二级缓存区域以mapper为单位划分,当一个商品信息变化会将全部商品信息的缓存数据所有清空。解决此类问题须要在业务层根据需求对数据有针对性缓存。
实现mybatis与spring进行整合,经过spring管理SqlSessionFactory、mapper接口。
mybatis官方提供与mybatis与spring整合jar包:
还包括其它jar:
spring3.2.0
mybatis3.2.7
dbcp链接池
数据库驱动
参考:
在classpath下建立mybatis/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>
<!—使用自动扫描器时,mapper.xml文件若是和mapper.java接口在一个目录则此处不用定义mappers -->
<mappers>
<package name="cn.itcast.mybatis.mapper" />
</mappers>
</configuration>
在classpath下建立applicationContext.xml,定义数据库连接池、SqlSessionFactory。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd ">
<!-- 加载配置文件 -->
<context:property-placeholder location="classpath:db.properties"/>
<!-- 数据库链接池 -->
<bean id="dataSource"class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="maxActive" value="10"/>
<property name="maxIdle" value="5"/>
</bean>
<!-- mapper配置 -->
<!-- 让spring管理sqlsessionfactory 使用mybatis和spring整合包中的 -->
<bean id="sqlSessionFactory"class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 数据库链接池 -->
<property name="dataSource" ref="dataSource" />
<!-- 加载mybatis的全局配置文件 -->
<property name="configLocation"value="classpath:mybatis/SqlMapConfig.xml" />
</bean>
</beans>
注意:在定义sqlSessionFactory时指定数据源dataSource和mybatis的配置文件。
使用此种方法即原始dao开发方法,须要编写dao接口,dao接口实现类、映射文件。
<mappers>
<mapper resource="mapper.xml文件的地址" />
<mapper resource="mapper.xml文件的地址" />
</mappers>
dao接口实现类方法中能够this.getSqlSession()进行数据增删改查。
<bean id=" "class="mapper接口的实现">
<property name="sqlSessionFactory" ref="sqlSessionFactory"></property>
</bean>
此方法即mapper接口开发方法,只需定义mapper接口,不用编写mapper接口实现类。每一个mapper接口都须要在spring配置文件中定义。
若是mapper.xml和mappre接口的名称相同且在同一个目录,这里能够不用配置
<mappers>
<mapper resource="mapper.xml文件的地址" />
<mapper resource="mapper.xml文件的地址" />
</mappers>
<bean id="" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="mapper接口地址"/>
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
此方法即mapper接口开发方法,只需定义mapper接口,不用编写mapper接口实现类。只须要在spring配置文件中定义一个mapper扫描器,自动扫描包中的mapper接口生成代代理对象。
注意mapper.xml的文件名和mapper的接口名称保持一致,且放在同一个目录
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="mapper接口包地址"></property>
<property name="sqlSessionFactoryBeanName"value="sqlSessionFactory"/>
</bean>
basePackage:扫描包路径,中间能够用逗号或分号分隔定义多个包
若是将mapper.xml和mapper接口的名称保持一致且放在一个目录 则不用在sqlMapConfig.xml中进行配置
使用官方网站的mapper自动生成工具mybatis-generator-core-1.3.2来生成po类和mapper映射文件。
在generatorConfig.xml中配置mapper生成的详细信息,注意改下几点:
配置文件以下:
详见generatorSqlmapCustom工程
Public void generator() throws Exception{
List<String> warnings = new ArrayList<String>();
boolean overwrite = true;
File configFile = new File("generatorConfig.xml");
ConfigurationParser cp = new ConfigurationParser(warnings);
Configuration config = cp.parseConfiguration(configFile);
DefaultShellCallback callback = new DefaultShellCallback(overwrite);
MyBatisGenerator myBatisGenerator = newMyBatisGenerator(config,
callback, warnings);
myBatisGenerator.generate(null);
}
Public static void main(String[] args) throws Exception {
try {
GeneratorSqlmap generatorSqlmap = new GeneratorSqlmap();
generatorSqlmap.generator();
} catch (Exception e) {
e.printStackTrace();
}
}
Mapper.xml的文件拷贝至mapper目录内
Mapper.java的文件拷贝至mapper 目录内
注意:mapper xml文件和mapper.java文件在一个目录内且文件名相同。
学会使用mapper自动生成的增、删、改、查方法。
//删除符合条件的记录
int deleteByExample(UserExample example);
//根据主键删除
int deleteByPrimaryKey(String id);
//插入对象全部字段
int insert(User record);
//插入对象不为空的字段
int insertSelective(User record);
//自定义查询条件查询结果集
List<User> selectByExample(UserExample example);
//根据主键查询
UserselectByPrimaryKey(String id);
//根据主键将对象中不为空的值更新至数据库
int updateByPrimaryKeySelective(User record);
//根据主键将对象中全部字段的值更新至数据库
int updateByPrimaryKey(User record);
XXXMapper.xml文件已经存在时,若是进行从新生成则mapper.xml文件内容不被覆盖而是进行内容追加,结果致使mybatis解析失败。
解决方法:删除原来已经生成的mapper xml文件再进行生成。
Mybatis自动生成的po及mapper.java文件不是内容而是直接覆盖没有此问题。
下边是关于针对oracle数据库表生成代码的schema问题:
Schma即数据库模式,oracle中一个用户对应一个schema,能够理解为用户就是schema。
当Oralce数据库存在多个schema能够访问相同的表名时,使用mybatis生成该表的mapper.xml将会出现mapper.xml内容重复的问题,结果致使mybatis解析错误。
解决方法:在table中填写schema,以下:
<table schema="XXXX" tableName=" " >
XXXX即为一个schema的名称,生成后将mapper.xml的schema前缀批量去掉,若是不去掉当oracle用户变动了sql语句将查询失败。
快捷操做方式:mapper.xml文件中批量替换:“from XXXX.”为空
Oracle查询对象的schema可从dba_objects中查询,以下:
select * from dba_objects