SSM三部曲之Mybatis篇

MyBatis 什么是 MyBatis? MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所

MyBatis

什么是 MyBatis?

MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。
MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。
MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和
Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

简单入门

1.导入依赖

有版本配置要求

org.mybatismybatis3.5.2mysqlmysql-connector-java5.1.47

2.MyBatis的核心设置





3.实现增删改查

创建实体类

public class User {private Integer id;private String userName;private Date birthday;private String sex;private String address;public User() {}public User(Integer id, String userName, Date birthday, String sex, String address) {this.id = id;this.userName = userName;this.birthday = birthday;this.sex = sex;this.address = 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;}@Overridepublic String toString() {return "User{" +"id=" + id +", userName='" + userName + '\'' +", birthday=" + birthday +", sex='" + sex + '\'' +", address='" + address + '\'' +'}';}
}

持久化层

public interface UserMapper {/*** 查询所有* @return*/List queryAllUSer();/*** 通过名字查询* @param name* @return*/List queryUserByName(String name);/*** 插入用户* @param user*/void insertUser(User user);/*** 更新用户* @param user*/void updateUser(User user);/*** 删除用户* @param id*/void deleteUser(Integer id);
}

编写sql语句




insert into user (id,username,birthday,sex,address) values(#{id},#{userName},#{birthday},#{sex},#{address})update user set username=#{userName},birthday=#{birthday},sex=#{sex},address=#{address} where id=#{id}delete from user where id=#{id}

Mybatis工具类

//SqlSessionFactory --->>  Sqlsession   工厂模式
public class MybatisUtil {private static SqlSessionFactory sqlSessionFactory;static{try {//使用Mybatis   第一步 获取SQL Session Factory对象String resource = "mybatis-config.xml";InputStream inputStream = Resources.getResourceAsStream(resource);  //字节流 reader字符流
//            Reader reader = Resources.getResourceAsReader(resource);sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);} catch (IOException e) {e.printStackTrace();}}// 既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例// SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。public static SqlSession getSqlSession(){return sqlSessionFactory.openSession();}
}

测试

public class MyTest {@Testpublic void getUserLIst(){//获取sqlSession  从sqlSession工厂中SqlSession sqlSession = MybatisUtil.getSqlSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);List users = mapper.queryAllUSer();for (User user : users) {System.out.println(user);}sqlSession.close();}@Testpublic void getUserByName(){//获取sqlSession  从sqlSession工厂中SqlSession sqlSession = MybatisUtil.getSqlSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);List userList = mapper.queryUserByName("喜羊羊");for (User user : userList) {System.out.println(user);}sqlSession.close();}@Testpublic void insertUser(){//获取sqlSession  从sqlSession工厂中SqlSession sqlSession = MybatisUtil.getSqlSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);mapper.insertUser(new User(7,"沸羊羊",new Date(),"男","羊村"));System.out.println(mapper.queryUserByName("沸羊羊"));//增删改必须    提交事务sqlSession.commit();sqlSession.close();}@Testpublic void updateUser(){//获取sqlSession  从sqlSession工厂中SqlSession sqlSession = MybatisUtil.getSqlSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);mapper.updateUser(new User(7,"舔狗",new Date(),"男","羊村"));//增删改必须    提交事务sqlSession.commit();sqlSession.close();}@Testpublic void deleteUser(){//获取sqlSession  从sqlSession工厂中SqlSession sqlSession = MybatisUtil.getSqlSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);mapper.deleteUser(7);//增删改必须    提交事务sqlSession.commit();sqlSession.close();}
}

在进行增删改操作时要提交事务,否则数据库信息不会变更,可以在设置openSesion(true)自动提交

配置configuration

必须按照这个顺序进行配置
configuration(配置)
properties(属性)
settings(设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境配置)
databaseIdProvider(数据库厂商标识)
mappers(映射器) 

properties(属性)

将属性在外部进行配置,就可以给配置提供更加灵活的选择。 db.properties

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/数据库?useUnicode=true&characterEncoding=UTF-8
username=username
password=password





设置(settings)

目前学习阶段常用到的两个设置 缓存和日志




第一个日志开启会在控制台打印输出执行的sql日志. 使用第二个LOG4J时要添加依赖,创建log4j.properties文件

##将等级为DEBUG的日志信思输出到console控制台和file文件这两个目的地,console和fiLe的定义在下面的代码
log4j.rootLogger=DEBUG,console,file#控制台输出的相关设置
log4j.appender.console = org.apache.log4j.ConsoleAppender
#输出方式
log4j.appender.console.Target = System.out
#输出级别
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
#输出格式
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n#文件输出的相关设置
log4j.appender.file = org.apache.log4j.RollingFileAppender
#输出文件的目录
log4j.appender.file.File=./log/mybatis.log
#输出文件的最大存储量
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n#日志输出级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG

缓存

要在映射文件中,应用二级缓存


类型别名(typeAliases)

类型别名可为 Java 类型设置一个缩写名字。它仅用于XML 配置,意在降低冗余的全限定类名书写。例如:



alias时别名,在写sql标签时中需要用到parameterType和resultType就可以使用别名。 也可以用package标签,MyBatis会在包名下面搜索需要的Java Bean。也可以用注解方式 @Alias("author")


环境配置(environments)

MyBatis可以配置成适应多种环境,这种机制有助于将SQL映射应用于多种数据库之中。尽管可以配置多个环境,但每个SqlSessionFactory实例只能选择一种环境。 所以,想连接两个数据库,就需要创建两 SqlSessionFactory 实例,每个数据库对应一个。每个数据库对应一个SqlSessionFactory实例 为了指定创建哪种环境,只要将它作为可选的参数传递给 SqlSessionFactoryBuilder.

public class MybatisUtil {private static SqlSessionFactory sqlSessionFactory;static{try {String resource = "mybatis-config.xml";InputStream inputStream = Resources.getResourceAsStream(resource);sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);} catch (IOException e) {e.printStackTrace();}}public static SqlSession getSqlSession(){return sqlSessionFactory.openSession();}
}

映射器(mappers)

映射器简单来说就是告诉MyBatis去哪里找sql语句,使用相对于类路径的资源引用,或完全限定资源定位符(包括 file:/// 形式的 URL),或类名和包名等。例如:




xml映射器

MyBatis 的真正强大在于它的语句映射,这是它的魔力所在。由于它的异常强大,映射器的 XML 文件就显得相对简单 MyBatis 致力于减少使用成本,让用户能更专注于SQL 代码。 增删改查主要对应 insert、delete、update、select标签 用select标签举例:

    

id属性后满是持久化层接口中的方法名且必须一致。

parameterType将会传入这条语句的参数的类全限定名或别名。 这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler)推断出具体传入语句的参数,默认值为未设置。 并且在传递多个参数或者类型不同的参数时可以使用map进行传参、或者是基于注解@Param()实现。 还有就是当数据库字段类型比较特殊的情况下使用 {property,javaType=int,jdbcType=NUMERIC} 指定一个特殊的数据类型。但是目前没有遇到需要使用的地方,但是应该也很重要吧。

resultType是返回的结果的限定名或别名。也可以使用resultMap结果集映射,通常在数据库字段名和实体类之端明不宜这情况下使用,个人感觉非常好用~

sql语句也可以写在持久化层中,在方法的上面加上@select、@insert、@update、@delete注解方式直接编写功能简单的sql。

动态sql

if标签

当面临可选择的条件查询时,就比如在我的博客后台中根据博客各种属性进行选择性的查询情况下使用。 if标签中的test属性后就是查询条件,当根据性别选项不为空的查询时就会在 sql语句后加上if中的条件。

List getUserListByIF(@Param("address") String address,@Param("sex") String sex);

choose、when、otherwise标签

有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用,就像java中的switch语句一样。 当when标签中的条件成立时,就只将when标签中的sql拼接起来,否者就拼接otherwise中的slq语句。

    @Testpublic void selectByCWO(){//获取sqlSession  从sqlSession工厂中SqlSession sqlSession = MybatisUtil.getSqlSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);HashMap map = new HashMap<>();map.put("address","羊村");
//        map.put("sex","男");mapper.getUserListByCWO(map);sqlSession.close();}

foreach批量增删改查

在xml中id in和Java中得到增强for循环一样使用,在foreach标签中将map、list等传进collection,item是当前迭代对象,index是下标索引,item值,index是键。 open="(" 是在slq中的in后添加‘(’,separator=","是以‘,’进行分割,close=")"以‘)’为结尾 完整的sql语句就成了:select * from user where id in (item1,item2,item3…)。

 delete from user where id in#{id}insert into user (id,username,age,birthday,sex,address) value(#{user.id},#{user.userName},#{user.age},#{user.birthday},#{user.sex},#{user.address})

底层原理浅析

每一个基于MyBatis应用的都是以一个SqlSessionFactory的实例为核心,而SqlSessionFactory是通过SqlSessionFactoryBuilder从xml配置文件中构建出来的。 在MyBatis中有一个Resounces工具类,它能够更好的帮助MyBatis解析xml文件。 再来看SqlSessionFactoryBuilder是如何build出SqlSessionFactory的。创造一个SqlSessionFactoryBuilder调用build,通过重载本类的build方法下面的的第一个build方法, 将读取的xml流放进XMLConfigBuilder构造方法中,实例出一个XMLConfigBuilder对象,在经过最下面的重载build方法实例出一个DefaultSqlSessionFactory, 这个DefaultSqlSessionFactory就成了最终的SqlSessionFactory。

  public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {SqlSessionFactory var5;try {XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);var5 = this.build(parser.parse());} catch (Exception var14) {throw ExceptionFactory.wrapException("Error building SqlSession.", var14);} finally {ErrorContext.instance().reset();try {inputStream.close();} catch (IOException var13) {}}return var5;}public SqlSessionFactory build(Configuration config) {return new DefaultSqlSessionFactory(config);}

那就再看看XMLConfigBuilder是如何解析xml的,在构造器中又构造了父类Configuration, 从中又看到了一些熟悉的字眼,应该就是在这一步将xml文件配置进行了解析存储进了configuration, 然后再将configuration给了DefaultSqlSessionFactory。

    private boolean parsed;private final XPathParser parser;private String environment;private final ReflectorFactory localReflectorFactory;public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);}private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {super(new Configuration());this.localReflectorFactory = new DefaultReflectorFactory();ErrorContext.instance().resource("SQL Mapper Configuration");this.configuration.setVariables(props);this.parsed = false;this.environment = environment;this.parser = parser;}
    protected Environment environment;protected boolean safeRowBoundsEnabled;protected boolean safeResultHandlerEnabled;protected boolean mapUnderscoreToCamelCase;protected boolean aggressiveLazyLoading;protected boolean multipleResultSetsEnabled;protected boolean useGeneratedKeys;protected boolean useColumnLabel;protected boolean cacheEnabled;protected boolean callSettersOnNulls;protected boolean useActualParamName;protected boolean returnInstanceForEmptyRow;protected String logPrefix;protected Class logImpl;protected Class vfsImpl;protected LocalCacheScope localCacheScope;protected JdbcType jdbcTypeForNull;protected Set lazyLoadTriggerMethods;protected Integer defaultStatementTimeout;protected Integer defaultFetchSize;protected ResultSetType defaultResultSetType;protected ExecutorType defaultExecutorType;protected AutoMappingBehavior autoMappingBehavior;protected AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior;protected Properties variables;protected ReflectorFactory reflectorFactory;protected ObjectFactory objectFactory;protected ObjectWrapperFactory objectWrapperFactory;protected boolean lazyLoadingEnabled;protected ProxyFactory proxyFactory;protected String databaseId;protected Class configurationFactory;protected final MapperRegistry mapperRegistry;protected final InterceptorChain interceptorChain;protected final TypeHandlerRegistry typeHandlerRegistry;protected final TypeAliasRegistry typeAliasRegistry;protected final LanguageDriverRegistry languageRegistry;protected final Map mappedStatements;protected final Map caches;protected final Map resultMaps;protected final Map parameterMaps;protected final Map keyGenerators;protected final Set loadedResources;protected final Map sqlFragments;protected final Collection incompleteStatements;protected final Collection incompleteCacheRefs;protected final Collection incompleteResultMaps;protected final Collection incompleteMethods;protected final Map cacheRefMap;public Configuration() {this.safeResultHandlerEnabled = true;this.multipleResultSetsEnabled = true;this.useColumnLabel = true;this.cacheEnabled = true;this.useActualParamName = true;this.localCacheScope = LocalCacheScope.SESSION;this.jdbcTypeForNull = JdbcType.OTHER;this.lazyLoadTriggerMethods = new HashSet(Arrays.asList("equals", "clone", "hashCode", "toString"));this.defaultExecutorType = ExecutorType.SIMPLE;this.autoMappingBehavior = AutoMappingBehavior.PARTIAL;this.autoMappingUnknownColumnBehavior = AutoMappingUnknownColumnBehavior.NONE;this.variables = new Properties();this.reflectorFactory = new DefaultReflectorFactory();this.objectFactory = new DefaultObjectFactory();this.objectWrapperFactory = new DefaultObjectWrapperFactory();this.lazyLoadingEnabled = false;this.proxyFactory = new JavassistProxyFactory();this.mapperRegistry = new MapperRegistry(this);this.interceptorChain = new InterceptorChain();this.typeHandlerRegistry = new TypeHandlerRegistry();this.typeAliasRegistry = new TypeAliasRegistry();this.languageRegistry = new LanguageDriverRegistry();this.mappedStatements = (new Configuration.StrictMap("Mapped Statements collection")).conflictMessageProducer((savedValue, targetValue) -> {return ". please check " + savedValue.getResource() + " and " + targetValue.getResource();});this.caches = new Configuration.StrictMap("Caches collection");this.resultMaps = new Configuration.StrictMap("Result Maps collection");this.parameterMaps = new Configuration.StrictMap("Parameter Maps collection");this.keyGenerators = new Configuration.StrictMap("Key Generators collection");this.loadedResources = new HashSet();this.sqlFragments = new Configuration.StrictMap("XML fragments parsed from previous mappers");this.incompleteStatements = new LinkedList();this.incompleteCacheRefs = new LinkedList();this.incompleteResultMaps = new LinkedList();this.incompleteMethods = new LinkedList();this.cacheRefMap = new HashMap();this.typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);this.typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);this.typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);this.typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);this.typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);this.typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);this.typeAliasRegistry.registerAlias("FIFO", FifoCache.class);this.typeAliasRegistry.registerAlias("LRU", LruCache.class);this.typeAliasRegistry.registerAlias("SOFT", SoftCache.class);this.typeAliasRegistry.registerAlias("WEAK", WeakCache.class);this.typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);this.typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);this.typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);this.typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);this.typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);this.typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);this.typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);this.typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);this.typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);this.typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);this.typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);this.typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);this.languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);this.languageRegistry.register(RawLanguageDriver.class);}

生命周期和作用域

SqlSessionFactoryBuilder:

  • 一旦创建了 SqlSessionFactory,就不再需要它了。
  • 局部变量

SqlSessionFactory:

  • 说白就是可以想象为:数据库连接池。
  • SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。
  • SqlSessionFactory 的最佳作用域是应用作用域。
  • 最简单的就是使用单例模式或者静态单例模式。

SqlSession:

  • 连接到连接池的一个请求!
  • SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。
  • 用完后需要赶紧关闭,否则资源被占用!