一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第11天,点击查看活动详情。
Mybatis对数据库操作的优势
JAVA编程免不了和数据库打交道,那么如何高效便捷地操作数据库,也是一个需要应对的问题,原生的基于JDBC的方式非常低效,而且要写一大堆无用的模板代码,不值得选取。ORM是对JDBC的封装,让我们不需要重复的造轮子,目前已经有很多优秀的ORM框架可供使用了,常见的比如Mybatis(batis)、Hibernate、Jpa、Jdo等。
优点:
- ORM是对JDBC的封装,从而解决了JDBC的各种存在问题,提高效率
- 使开发更加对象化
- 可移植性强
- 可以很方便地引入数据缓存之类的附加功能
缺点:
- 自动化进行关系数据库的映射需要消耗少量系统性能。
- 在处理多表联查、where条件复杂之类的查询时,ORM的语法会变得复杂。
市面上主流ORM框架:
- EJB:重量级、高花费的ORM技术,支持JPA,尤其是EJB3低侵入式 的设·计,增加了Annotation
- Hibernate:开源,支持JPA ,被选作JBoss的持久层解决方案
- iBatis:”SQL Mapping”框架,Apache软件基金组织的子项目,后 转Google Code旗下,ibatis3.x正式更名为Mybatis
- Spring Data JPA:Spring框架中的子模块
- TopLink:Oracle公司的产品
- Open JPA:Apache软件基金组织的开源项目
Mybatis都有哪些优势?
- Mybatis入门简单;在使用上,对于熟悉编写SQL的同学来说,基本上是即学即用。
Mybatis对jdbc的抽象封装程度更高,spring jdbc要想实现的细节很多,例如Mybatis封装了更多的对象映射。支持注解,面对接口开发,效率高,分分钟解决一个sql。- 对于复杂的SQL,springJDBC编写麻烦,动态SQL语句设计也麻烦,相比之下,Mybatis更加灵活且人性化。
- mybatis的高度封装,使得程序员可专注与业务层,开发效率高。所以选择mybatis的开发公司多。
相比较Hibernate与Mybatis的区别?
Hibernate与MyBatis都可以是通过SessionFactoryBuider由XML配置文件生成SessionFactory,然后由SessionFactory 生成Session,最后由Session来开启执行事务和SQL语句。其中SessionFactoryBuider,SessionFactory,Session的生命周期都是差不多的。
Hibernate和MyBatis都支持JDBC和JTA事务处理。
Mybatis优势
- MyBatis可以进行更为细致的SQL优化,可以减少查询字段。
- MyBatis容易掌握,而Hibernate门槛较高。
Hibernate优势
- Hibernate的DAO层开发比MyBatis简单,Mybatis需要维护SQL和结果映射。
- Hibernate对对象的维护和缓存要比MyBatis好,对增删改查的对象的维护要方便。
- Hibernate数据库移植性很好,MyBatis的数据库移植性不好,不同的数据库需要写不同SQL。
- Hibernate有更好的二级缓存机制,可以使用第三方缓存。MyBatis本身提供的缓存机制不佳。
Hibernate
- Hibernate功能强大,数据库无关性好,O/R映射能力强,如果你对Hibernate相当精通,而且对Hibernate进行了适当的封装,那么你的项目整个持久层代码会相当简单,需要写的代码很少,开发速度很快,非常爽。
- Hibernate的缺点就是学习门槛不低,要精通门槛更高,而且怎么设计O/R映射,在性能和对象模型之间如何权衡取得平衡,以及怎样用好Hibernate方面需要你的经验和能力都很强才行。
MyBatis
- MyBatis入门简单,即学即用,提供了数据库查询的自动对象绑定功能,而且延续了很好的SQL使用经验,对于没有那么高的对象模型要求的项目来说,相当完美。
- MyBatis的缺点就是框架还是比较简陋,功能尚有缺失,虽然简化了数据绑定代码,但是整个底层数据库查询实际还是要自己写的,工作量也比较大,而且不太容易适应快速数据库修改。
Hibernate与Mybatis 的缓存机制都有哪些区别?
相同点:
Hibernate和Mybatis的二级缓存除了采用系统默认的缓存机制外,都可以通过实现你自己的缓存或为其他第三方缓存方案,创建适配器来完全覆盖缓存行为。
不同点:
Hibernate的二级缓存配置在SessionFactory生成的配置文件中进行详细配置,然后再在具体的表-对象映射中配置是那种缓存。
MyBatis的二级缓存配置都是在每个具体的表-对象映射中进行详细配置,这样针对不同的表可以自定义不同的缓存机制。并且Mybatis可以在命名空间中共享相同的缓存配置和实例,通过Cache-ref来实现。
下面通过对数据的增删改查来看出mybatis的优势
1.mybatis对数据库的增删改查
数据库的建立和User实体类的创建
package com.mybatisD.domain;
import java.io.Serializable;
import java.util.Date;
public class User implements Serializable {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
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 Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", birthday=" + birthday +
", sex='" + sex + '\'' +
", address='" + address + '\'' +
'}';
}
}
配置IUserDao.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.mybatisD.dao.IUserDao">
<!--配置查询所有-->
<select id="findAll" resultType="com.mybatisD.domain.User">
select * from user
</select>
<!--保存用户-->
<insert id="saveUser" parameterType="com.mybatisD.domain.User">
<!-- 配置插入操作后,获取插入数据id-->
<selectKey keyProperty="id" keyColumn="id" resultType="int" order="AFTER">
select last_insert_id();
</selectKey>
insert into user (Username,address,sex,birthday)values(#{username},#{address},#{sex},#{birthday});
</insert>
<!--更新用户-->
<update id="updateUser" parameterType="com.mybatisD.domain.User">
update user set username=#{username},address=#{address},sex=#{sex},birthday=#{birthday} where id=#{id}
</update>
<!--删除用户-->
<delete id="deleteUser" parameterType="java.lang.Integer">
delete from user where id=#{userid}
</delete>
<!--根据ID查询用户-->
<select id="findById" parameterType="INT" resultType="com.mybatisD.domain.User">
select * from user where id=#{userid}
</select>
<!--更具名称模糊查寻-->
<select id="findByName" parameterType="string" resultType="com.mybatisD.domain.User">
select * from user where username like #{name}
</select>
<!--获取用户总计录-->
<select id="findTotal" resultType="int">
select count(id) from user
</select>
<!--更具QueryVo的条件查询用户使用OJN表达式-->
<select id="findUserByVo" parameterType="com.mybatisD.domain.QueryVo" resultType="com.mybatisD.domain.User">
select * from USER where username like #{user.username}
</select>
</mapper>
配置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>
<!--配置环境-->
<environments default="mysql">
<!--配置mysql环境-->
<environment id="mysql">
<!--配置事物类型-->
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<!--配置连接数据库的4个信息-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/newsql?characterEncoding=utf-8"/>
<property name="username" value="root"/>
<property name="password" value=""/>
</dataSource>
</environment>
</environments>
<!--指定映射配置文件的位置,映射配置文件指的是每个到独立的配置文件-->
<mappers>
<mapper resource="com/mybatisD/dao/IUserDao.xml"/>
</mappers>
<!--如果要是用注解来配置的话,此处应该使用class属性指定被注解的dao全限定类名-->
<!--<mappers>
<mapper class="com.mybatisD.dao.IUserDao"/>
</mappers>-->
</configuration>
创建log4j.properties日志
log4j.rootCategory=debug,CONSOLE, LOGFILE
log4j.logger.org.apache.axis.enterprise=FATAL, CONSOLE
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x -%m\n
log4j.appender.LOGFILE=org.apache.log4j.FileAppender
log4j.appender.LOGFILE.File=d:\axis.log
log4j.appender.LOGFILE.Append=true
log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.LOGFILE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x -%m\n
创建测试类test
package com.mybatisJ.test;
import com.mybatisD.dao.IUserDao;
import com.mybatisD.domain.QueryVo;
import com.mybatisD.domain.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.After;
import org.junit.Before;
import org.junit.Test;
import java.io.InputStream;
import java.util.Date;
import java.util.List;
/**
* @author :程序员徐大大
* @description:TODO
* @date :2022-01-20 21:55
*/
public class MybatisTest {
private InputStream in;
private SqlSession sqlSession;
private IUserDao userDao;
@Before//用于在测试方法执行之前执行
public void init()throws Exception{
in = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
sqlSession = factory.openSession();
//4.使用SqlSession创建Dao接口的代理对象
userDao = sqlSession.getMapper(IUserDao.class);
}
@After//用于在测试方法之后执行
public void destory() throws Exception{
//添加提交事物,不添加无法提交
sqlSession.commit();
sqlSession.close();
in.close();
}
@Test
public void testFindAll() throws Exception{
List<User> users = userDao.findAll();
for (User user: users) {
System.out.println(user);
}
}
//添加用户对象
@Test
public void testSave(){
User user = new User();
user.setUsername("测试用户对象");
user.setAddress("湖南");
user.setSex("男");
user.setBirthday(new Date());
//执行保存方法
userDao.saveUser(user);
System.out.println(user);
}
//更新操作
@Test
public void testupdateUser(){
User user = new User();
user.setId(5);
user.setUsername("更新测试用户");
user.setAddress("北京");
user.setSex("女");
user.setBirthday(new Date());
//执行保存方法
userDao.updateUser(user);
}
//执行删除方法
@Test
public void testdeleteUser(){
userDao.deleteUser(6);
}
//执行查询一个用户的方法
@Test
public void testFindOne(){
User user = userDao.findById(5);
System.out.println(user);
}
//执行名称的模糊查询
@Test
public void testFindByName(){
List<User> user= userDao.findByName("%王%");
System.out.println(user);
}
//执行查询总数
@Test
public void testFindTotal(){
int count = userDao.findTotal();
System.out.println(count);
}
//测试使用Query作为查询条件
@Test
public void testFindByVo(){
QueryVo vo = new QueryVo();
User user = new User();
user.setUsername("%王%");
vo.setUser(user);
List<User> users = userDao.findUserByVo(vo);
for (User u: users) {
System.out.println(u);
}
}
}
其中的一处测试涉及到一个实现类QueryVo
package com.mybatisD.domain;
/**
* @author :程序员徐大大
* @description:TODO
* @date :2022-01-21 16:05
*/
public class QueryVo {
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
Maven的pom.xml配置导入依赖
<?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.myBatisDemo</groupId>
<artifactId>MyBatis_Demo</artifactId>
<version>1.0-SNAPSHOT</version>
<!--打包方式-->
<packaging>jar</packaging>
<!--db-->
<!--基本的maven配置就完成了 现在添加使用Mybatis的基本配置-->
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.3.0</version>
</dependency>
<!--其他的基本配置,日志、单元测试、jdbc——jar包-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.38</version>
</dependency>
<!--<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.12</version>
</dependency>-->
<!--<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.12</version>
</dependency>-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
<!--配置编译源代码的jdk版本-->
<!-- <build>-->
<!-- <plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>-->
<!-- <resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**.*.xml</include>
</includes>
</resource>
</resources>-->
<!-- </build>-->
</project>
其中注意:java中与MySQL里面的各个字段不对应时可以进行下面的操作
<!--配置查询结果的列名和实体类的属性名的对应关系-->
<resultMap id="userMap" type="com.mybatisD.domain.User">
<!--主键字段的对应-->
<id property="userId" column="id"></id>
<!--非主键字段的对应-->
<result property="userName" column="username"></result>
<result property="userAddress" column="address"></result>
<result property="userSex" column="sex"></result>
<result property="userBirthday" column="birthday"></result>
</resultMap>
也可以单独进行实现其中dao的方法如创建实现类UserDaoImpl
package com.mybatisD.dao.impl;
import com.mybatisD.dao.IUserDao;
import com.mybatisD.domain.QueryVo;
import com.mybatisD.domain.User;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import java.util.List;
/**
* @author :程序员徐大大
* @description:TODO
* @date :2022-01-21 16:49
*/
public class UserDaoImpl implements IUserDao {
private SqlSessionFactory factory;
public UserDaoImpl(SqlSessionFactory factory) {
this.factory = factory;
}
public List<User> findAll() {
//1.根据factory获取SqlSession对象
SqlSession session = factory.openSession();
//2.调用SqlSession中的方法,实现查询列表
List<User> users = session.selectList("com.mybatisD.dao.IUserDao.findAll");
//3.释放资源
session.close();
return users;
}
public void saveUser(User user) {
//1.根据factory获取SqlSession对象
SqlSession session = factory.openSession();
//2.调用方法实现保存
session.insert("com.mybatisD.dao.IUserDao.saveUser",user);
//3.提交事物
session.commit();
//4.释放资源
session.close();
}
public void updateUser(User user) {
//1.根据factory获取SqlSession对象
SqlSession session = factory.openSession();
//2.调用方法实现保存
session.update("com.mybatisD.dao.IUserDao.updateUser",user);
//3.提交事物
session.commit();
//4.释放资源
session.close();
}
public void deleteUser(Integer userId) {
//1.根据factory获取SqlSession对象
SqlSession session = factory.openSession();
//2.调用方法实现保存
session.update("com.mybatisD.dao.IUserDao.deleteUser",userId);
//3.提交事物
session.commit();
//4.释放资源
session.close();
}
public User findById(Integer userId) {
//1.根据factory获取SqlSession对象
SqlSession session = factory.openSession();
//2.调用SqlSession中的方法,实现查询列表
User users = session.selectOne("com.mybatisD.dao.IUserDao.findById",userId);
//3.释放资源
session.close();
return users;
}
public List<User> findByName(String username) {
//1.根据factory获取SqlSession对象
SqlSession session = factory.openSession();
//2.调用SqlSession中的方法,实现查询列表
List<User> users = session.selectList("com.mybatisD.dao.IUserDao.findByName",username);//参数就是获取配置信息的key
//3.释放资源
session.close();
return users;
}
public int findTotal() {
//1.根据factory获取SqlSession对象
SqlSession session = factory.openSession();
//2.调用SqlSession中的方法,实现查询列表
Integer count = session.selectOne("com.mybatisD.dao.IUserDao.findTotal");
//3.释放资源
session.close();
return count;
}
public List<User> findUserByVo(QueryVo vo) {
return null;
}
}
其中的测试类就需要进行一下改变,其它的配置不变
package com.mybatisJ.test;
import com.mybatisD.dao.IUserDao;
import com.mybatisD.dao.impl.UserDaoImpl;
import com.mybatisD.domain.QueryVo;
import com.mybatisD.domain.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.After;
import org.junit.Before;
import org.junit.Test;
import java.io.InputStream;
import java.util.Date;
import java.util.List;
/**
* @author :程序员徐大大
* @description:TODO
* @date :2022-01-20 21:55
*/
public class MybatisTest {
private InputStream in;
private IUserDao userDao;
@Before//用于在测试方法执行之前执行
public void init()throws Exception{
//1.读取配置文件,生成字节输入流
in = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//2.获取SqlSessionFactory
SqlSessionFactory factory = builder.build(in);
//3.使用工厂对象,创建dao对象
userDao = new UserDaoImpl(factory);
}
@After//用于在测试方法之后执行
public void destory() throws Exception{
//添加提交事物,不添加无法提交
in.close();
}
@Test
public void testFindAll() throws Exception{
List<User> users = userDao.findAll();
for (User user: users) {
System.out.println(user);
}
}
//添加用户对象
@Test
public void testSave(){
User user = new User();
user.setUsername("测试用户对象");
user.setAddress("湖南");
user.setSex("男");
user.setBirthday(new Date());
//执行保存方法
userDao.saveUser(user);
System.out.println(user);
}
//更新操作
@Test
public void testupdateUser(){
User user = new User();
user.setId(7);
user.setUsername("更新测试用户");
user.setAddress("北京");
user.setSex("女");
user.setBirthday(new Date());
//执行保存方法
userDao.updateUser(user);
}
//执行删除方法
@Test
public void testdeleteUser(){
userDao.deleteUser(8);
}
//执行查询一个用户的方法
@Test
public void testFindOne(){
User user = userDao.findById(5);
System.out.println(user);
}
//执行名称的模糊查询
@Test
public void testFindByName(){
List<User> user= userDao.findByName("%王%");
System.out.println(user);
}
//执行查询总数
@Test
public void testFindTotal(){
int count = userDao.findTotal();
System.out.println(count);
}
}
对外部配置数据库信息jdbcConfig.properties的引入
<?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属性:用于指定配置文件的位置,是按照类路径的写法来写平且必须存在与类路径下
-->
<properties resource="jdbcConfig.properties">
<!--<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/newsql?characterEncoding=utf-8"/>
<property name="username" value="root"/>
<property name="password" value=""/>-->
</properties>
<!--配置环境-->
<environments default="mysql">
<!--配置mysql环境-->
<environment id="mysql">
<!--配置事物类型-->
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<!--配置连接数据库的4个信息-->
<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>
<mapper resource="com/mybatisD/dao/IUserDao.xml"/>
</mappers>
<!--如果要是用注解来配置的话,此处应该使用class属性指定被注解的dao全限定类名-->
<!--<mappers>
<mapper class="com.mybatisD.dao.IUserDao"/>
</mappers>-->
</configuration>
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/newsql?characterEncoding=utf-8
jdbc.username=root
jdbc.password=
其中url属性的描述:uniform Resource Locator 统一资源定位符,它是可以唯一标识一个资源的位置 它的写法:http://ip:port/资源路径/jdbcConfig.properties
URI:uniform Resource Identifier 统一资源标识符,它是在应用中可以唯一定位一个资源 file:路径 或者 文件路径 标签typeAliases:可以起别名
<!--使用typeAlias配置别名,它只能配置domain类中的别名-->
<typeAliases>
<!--typeAliases用于配置别名,type属性指定的是实体类全限定类名,alias属性指定别名,当指定了别名就再区分大小写-->
<typeAlias type="com.mybatisD.domain.User" alias="user"></typeAlias>
<!--用于指定配置别名的包,当指定之后,该包下的实体类都会注册别名,并且类名就是别名,不再区分大小写-->
<package name="com.mybatisD.domain"/>
</typeAliases>
<mappers>
<!--package标签用于指定doa接口所在的包,当指定了之后就不需要在写mapper以及resource或者class了-->
<package name="com.mybatisD.domain"/>
</mappers>