很多互联网公司放弃使用Hibernate, 改用MyBatis; 理由是MyBatis比较轻巧, 可以做SQL语句优化。PS: 想想DBA的工作吧, 大数量级的数据库操作需要专业的DBA做SQL语句优化。 Hibernate封装比较完善,导致它不够灵活。 我们需要的是减化数据库操作的编码量,对JDBC进行必要的封装即可。
Hibernate和Mybatis都提供了缓存功能, 但实际项目开发中更多的是使用中间件如Redis来做缓存;Mybatis最大的优势是在Java代码里生成复杂的SQL语句,Hibernate做不到。
MyBatis官方说明: http://www.mybatis.org/mybatis-3/zh/statement-builders.html
百度百科上的介绍:https://baike.baidu.com/item/MyBatis/2824918?fr=aladdin
项目结构:
在pom.xml中添加junit,mybatis和log4j的maven仓库:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.brycegao.mybatis</groupId>
<artifactId>mybatis_demo</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>6.0.6</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
</project>
log4j默认会读取名为log4j.properties的文件。
MyBatis是对JDBC的封装,建议不要用MyBatis创建表。 我先电脑上启动MySQL服务, 并创建一个名为mybatis的数据库和一个user表。
jdbc.properties文件里填写jdbc的各个属性:
jdbc.driverClassName=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/mybatis?characterEncoding=utf8&useSSL=true
jdbc.username=root
jdbc.password=111111
设置mybatis-config.xml的属性(主要是jdbc和mapper标签):
<?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="jdbc.properties"/>
<!-- mybatis的各个配置项 -->
<settings>
<setting name="cacheEnabled" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="multipleResultSetsEnabled" value="true"/>
<setting name="useColumnLabel" value="true"/>
<setting name="useGeneratedKeys" value="false"/>
<setting name="autoMappingBehavior" value="PARTIAL"/>
<setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
<setting name="defaultExecutorType" value="SIMPLE"/>
<setting name="defaultStatementTimeout" value="25"/>
<setting name="defaultFetchSize" value="100"/>
<setting name="safeRowBoundsEnabled" value="false"/>
<setting name="mapUnderscoreToCamelCase" value="false"/>
<setting name="localCacheScope" value="SESSION"/>
<setting name="jdbcTypeForNull" value="OTHER"/>
<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>
<!-- 别名可以用在数据类型参数里,其实没啥用, 直接用原数据类型更直观! -->
<typeAliases>
<typeAlias alias="User" type="com.brycegao.mybatis.entity.User"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</dataSource>
</environment>
</environments>
<mappers>
<!-- 跟Dao类对应的xml文件,编写SQL语句 -->
<mapper resource="mappers/user_mapper.xml" />
</mappers>
</configuration>
user-mapper.xml里编写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">
<!-- 一个interface对应一个mapper.xml文件 -->
<mapper namespace="com.brycegao.mybatis.dao.UserDao">
<!-- 查询语句 根据关键字查询记录, select id值必须跟UserDao方法名相同-->
<select id="findUserById" parameterType="Integer" resultType="com.brycegao.mybatis.entity.User">
select * from user where id=#{id}
</select>
<!-- 查询语句 注意:返回值类型是数组时,仍然使用item数据类型 -->
<select id="findUsersAll" resultType="com.brycegao.mybatis.entity.User">
select * from user
</select>
<!-- User是在mybatis-config.xml里声明的别名 -->
<!-- useGeneratedKeys表示是否自动生成主键值, keyProperty是主键列名 -->
<insert id="addUser" parameterType="User"
useGeneratedKeys="true" keyProperty="id" >
insert into `user`(name, age, address, job, idno, mobile)
values(#{name},#{age},#{address},#{job},#{idno},#{mobile})
</insert>
<!-- 更新记录 User是别名-->
<update id="updateUser" parameterType="User" flushCache="true">
update user set name=#{name}, age=#{age}, address=#{address}, job=#{job}, idno=#{idno}, mobile=#{mobile} where id=#{id}
</update>
<!-- 根据主键删除一条记录 SQL不区分大小写。 注意java代码的deleteUser函数参数跟SQL语句的变量相同-->
<delete id="deleteUser" parameterType="int">
DELETE FROM user WHERE id=#{id}
</delete>
</mapper>
注意:
1、 select id要跟Java方法名相同, parameterType跟Java方法参数类型相同;
2、 namespace是Java中的接口类,Mybatis通过注解实现函数体;
3、 每个insert、update、select和delete标签对应一个sqlsession,可以单独配置超时、刷内存等等属性;
完整的junit测试类:注意在数据库操作完成后要释放SqlSession, 即调用其close方法,否则内存泄漏。 测试类在@Before里获取SqlSession实例, 在@Test里操作SqlSession实例, 在@After里释放SqlSession。
public class UserTest {
private final static Logger LOG = LoggerFactory.getLogger(UserTest.class);
private SqlSession session;
@Before
public void init() {
session = SqlSessionFactoryUtil.getSqlSession();
}
@Test
public void testQueryById() {
UserDao userDao = session.getMapper(UserDao.class);
User cur = userDao.findUserById(3); //执行SQL语句:select * from user where id=3
if (cur != null) {
LOG.debug(cur.toString());
}
User curExt = userDao.findUserBydIdExt(3);
if (curExt != null) {
LOG.debug(curExt.toString());
}
}
@Test
public void testQueryAll() {
UserDao userDao = session.getMapper(UserDao.class);
List<User> list = userDao.findUsersAll(); //执行SQL语句:select * from user
if (list != null) {
System.out.println("记录总数:" + list.size());
}
List<User> all = userDao.getUsers();
if (all != null) {
System.out.println("记录总数:" + all.size());
}
}
@Test
public void testAddUser() {
UserDao userDao = session.getMapper(UserDao.class);
//第一种插入方法, 缺点是无返回值,不知道是否插入成功
/*
User user1 = new User();
user1.setAge(31);
user1.setName("张一");
user1.setAddress("天津");
userDao.addUser(user1);
session.commit(); //必须执行该语句!
*/
//第二种插入方法,推荐使用! 有返回值,能判断是否插表成功
User user2 = new User();
user2.setAge(32);
user2.setName("张二");
user2.setAddress("天津");
//第一个参数是user_mapper.xml的namespace和id值拼起来
int resultCount = session.insert("com.brycegao.mybatis.dao.UserDao.addUser", user2); //如果插入成功,user2对象的id字段非0
System.out.printf("新插入记录id :%d ,插入记录数:%d ", user2.getId(), resultCount); //获取插入对象的id
session.commit(); //数据有变化时必须同步到数据库, 否则只是刷新了缓存!
}
@Test
public void testUpdateUser() {
UserDao userDao = session.getMapper(UserDao.class);
//找到表中任意一条记录,然后修改它并回写数据库
User cur = userDao.findUserById(3); //执行SQL语句:select * from user where id=3
System.out.println("testUpdateUser修改前:" + cur.getName());
cur.setAge(77);
//第一种方法
userDao.updateUser(cur);
session.commit();
//第二种方法
//int count = session.update("com.brycegao.mybatis.dao.UserDao.updateUser", cur); //count表示更新的记录数
//session.commit();
//System.out.println("更新总数:" + count);
User curExt = userDao.findUserById(3); //执行SQL语句:select * from user where id=3
System.out.println("testUpdateUser修改后:" + curExt.getName());
}
@Test
public void testDeleteUser() {
UserDao userDao = session.getMapper(UserDao.class);
//使用sql gui工具查看user表中的记录,随便挑一条记录
//第一种方法 无返回值!
userDao.deleteUser(6);
session.commit();
//第二种方法 count是成功删除的记录数
int count = session.delete("com.brycegao.mybatis.dao.UserDao.deleteUser", 6);
session.commit();
}
@After
public void deinit() {
session.close();
}
}
分页查询的实现方法:
1、 使用SQL的limit关键字实现,通过区间限制即可实现分页查询功能;优点是不费内存,缺点是每次都要从数据库查询。
取前10条记录: select * from user limit 10
取0~5条记录: select * from user limit 0,5
取5~10条记录: select * from user limit 5,10
2、 将表的内容缓存到内存, 每次从内存中读取; 一般是缓存到中间件Redis里, 优点是查询快,缺点是浪费内存。
在@Test函数体里点击鼠标左键, 然后选中Debug调试就可以了:
简要说一下Mybatis的动态SQL,即在Java代码里生成SQL语句; 支持生成增删改查语句, 下面以查询为例:
1、 创建一个java文件, 每个函数都可以返还一个SQL语句字符串。 注意看Mybatis的语法很清晰,这也是Mybatis的一大优势!!! Mybatis在xml文件里可以实现相同功能,即根据条件判断生成复杂的SQL语句。
public class UserSqlBuilder {
//根据姓名查询记录
public String buildGetUsersByName(final String name) {
return new SQL() {{ //是2个大括号
SELECT("*");
FROM("user");
if (name != null) { //支持条件判断
WHERE("name like #{name} || '%' "); //#{...}跟@Param的值一致
}
ORDER_BY("id");
}}.toString();
}
}
2、在UserDao.java里添加声明,
//使用UserSqlBuilder.java的buildGetUsersByName方法
@SelectProvider(type= UserSqlBuilder.class, method="buildGetUsersByName")
List<User> getUserByName(String name);
3、使用:
UserDao userDao = session.getMapper(UserDao.class);
List<User> list = userDao.getUserByName("张三");
demo代码:http://download.csdn.net/download/brycegao321/9956809