深入MyBatis开发之mybatis配置

      MyBatis,Java互联网时代的首选持久层框架。一般推荐使用XML配置的方式,因为注解方式不利于SQL的维护和编写。

MyBatis3官方文档:http://www.mybatis.org/mybatis-3/zh/configuration.html

本blog实例代码:https://github.com/JeeLearner/learning-ssmr/tree/master/chapter04

mybatis-config.xml配置

前言:MyBatis配置文件并不复杂,但是注意顺序不要颠倒!!



 
	 
	 
	 
	 
	 
	 
	 
		 
			 
			 
		
	
	 
	 


一、属性配置

三种方式:1.property子元素  2.properties文件  3.程序代码传递

注意:这三种方式是有优先级的,mybatis根据优先级来覆盖原先配置的属性值

优先级:程序传递>文件>子元素

前两种方式:




    
	
    
    
    

    
    
        
            
            
                
                
                
                
            
        
    
jdbc.properties
database.driver=com.mysql.jdbc.Driver
database.url=jdbc:mysql://localhost:3306/test-ssmr
database.username=root
database.password=root

方式三:程序代码传递

场景:运维人员为了保密,一般都需要把用户名和密码经过加密成为密文后,配置到properties文件中。对于开发人员和其他人员就需要通过解密才能得到真实的用户和密码。那么我们在创建SqlSessionFactory之前就需要把用户名和密码解密,然后把解密后的字符串重置到properties属性中。

public class SqlSessionFactoryUtil {

    private final static Class LOCK = SqlSessionFactoryUtil.class;

    private static SqlSessionFactory sqlSessionFactory = null;
    //构造函数私有化
    private SqlSessionFactoryUtil(){

    }

    public static SqlSessionFactory getSqlSessionFactory(){
        synchronized (LOCK){
            if (sqlSessionFactory != null){
                return sqlSessionFactory;
            }
            String resource = "mybatis-config.xml";
            InputStream inputStream;
            try {
                InputStream in = Resources.getResourceAsStream("jdbc.properties");
                Properties props = new Properties();
                props.load(in);
                String username = props.getProperty("database.username");
                String password = props.getProperty("batabase.password");
                //解密用户名和密码,并在属性中重置
                props.put("database.username", CodeUtils.decode(username));
                props.put("database.password", CodeUtils.decode(password));
                inputStream = Resources.getResourceAsStream(resource);
                // 使用程序传递的方式覆盖原有的properties属性参数
                sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream, props);
            } catch (IOException e){
                e.printStackTrace();
                return null;
            }
            return sqlSessionFactory;
        }
    }
}


二、属性配置

最复杂的配置,影响mybatis底层运行,但是大部分情况使用默认值即可。

下面给出一个全量的配置样例:




    
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
    


三、别名

别名分为系统定义别名和自定义别名。

注意:在MyBatis中别名不区分大小写。

1.系统定义别名

使用TypeAliasRefistry类的registerAlias方法就可以注册别名。一般是通过Configuration获取TypeAliasRegistry类对象,如configuration.getTypeAliasRegistry()。系统别名定义好了哪些?可以参考网络资料。

2.自定义别名




    
    
         
        
    
    
    
        
    
扫描别名的方式是将其第一个字母变为小写作为其别名,如role,user。这样如意出现别名重复的问题如


     
    
这样就会出现异常。这时可以用@Alias(user3)进行区分

@Alias("user3")
public class User {
    
}

四、类型转换器

在typeHandler汇总,分为jdbcType和javaType,typeHandler就是负责jdbcType和javaType之间的相互转换。

1.系统定义的typeHandler

    public interface TypeHandler中四个方法。

    public abstract class BaseTypeHandler extends TypeReference implements TypeHandler实现方法

    常用的StringTypeHandler: extends BaseTypeHandler

    MyBatis采用org.apache.ibatis.type.TypeHandlerRegistry类对象的register方法进行注册。

2.自定义TypeHandler

     MyBatis的TypeHandler就能应付一般的场景,但有时候不够用,如枚举(有特殊转换规则)

  2.1仿造StringTypeHandler来实现。

    (1)编写MyTypeHandler.java

public class MyTypeHandler implements TypeHandler {
    Logger logger = Logger.getLogger(MyTypeHandler.class);

    @Override
    public void setParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
        logger.info("设置string参数【" + parameter+"】");
        ps.setString(i, parameter);
    }

    @Override
    public String getResult(ResultSet rs, String columnName) throws SQLException {
        String result = rs.getString(columnName);
        logger.info("读取string参数1【" + result+"】");
        return result;
    }

    @Override
    public String getResult(ResultSet rs, int columnIndex) throws SQLException {
        String result = rs.getString(columnIndex);
        logger.info("读取string参数2【" + result+"】");
        return result;
    }

    @Override
    public String getResult(CallableStatement cs, int columnIndex) throws SQLException {
        String result = cs.getString(columnIndex);
        logger.info("读取string参数3【" + result+"】");
        return result;
    }
}
    (2)mybatis-config.xml配置typeHandler (注册)


    
   (3)显示启用自定义TypeHandler
有两种方式:①指定与MyTypeHandler一致的jdbcType和javaType;②直接使用typeHandler指定具体的实现类。




    
        
        
        
    

    

    

	
有时候由于枚举类型很多,系统需要的typeHandler也会很多,如果采用配置也会很麻烦,这是可以考虑使用包扫描的形式:


    
    
这样就没法指定javaType和jdbcType了,我们可以使用注解来处理他们:

//启用扫描注册的时候需要注解
@MappedTypes(String.class)
@MappedJdbcTypes(JdbcType.VARCHAR)
public class MyTypeHandler implements TypeHandler {

2.2枚举typeHandler

在绝大多数情况下,typeHandler因为枚举而使用,MyBatis已经定义了两个类作为枚举类型的支持,它们是:EnumOrdinalTypeHandler和EnumTypeHandler

2.2.1EnumOrdinalTypeHandler

     EnumOrdinalTypeHandler是按MyBatis根据枚举数组下标索引的方式进行匹配的,也是枚举类型的默认转换类,它要求数据库返回一个整数作为其下标,它会根据下标找到对应的枚举类型。

(1)创建性别枚举类SexEnum和用户POJO类User

public enum SexEnum {

    NALE(1, "男"),
    FENALE(0, "女");

    private int id;
    private String name;

    SexEnum(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public static SexEnum getSexById(int id) {
        for (SexEnum sex : SexEnum.values()) {
            if (sex.getId() == id) {
                return sex;
            }
        }
        return null;
    }

    /**getter/setter*/
}
public class User {
	private Long id;
	private String userName;
	private String password;
	private SexEnum sex;
	private String mobile;
	private String tel;
	private String email;
	private String note;

	/** setter and getter **/
}
(2)使用EnumOrdinalTypeHandler定义UserMapper.xml





    
        
        
        
        
        
        
        
        
    

    
(3)测试

此时数据库sex字段是char类型,有一条数据(值为1,代表女性)

private static void testTypeHandler() {
    Logger log = Logger.getLogger(Main.class);
    SqlSession sqlSession = null;
    try {
        sqlSession = SqlSessionFactoryUtil.openSqlSession();
        UserDao userDao = sqlSession.getMapper(UserDao.class);
        User user = userDao.getUser(1L);
        System.out.println(user.getSex().getName());
    } finally {
        if (sqlSession != null) {
            sqlSession.close();
        }
    }
}

2.2.2EnumTypeHandler

EnumTypeHandler会把使用的名称转化为对应的枚举,比如它会根据数据库返回的字符串“MALE”,进行Enum.valueOf(SexEnum.class, "MALE");转换,所以为了测试EnumTypeHandler的转换,需要把数据库的sex字段修改为字符型(varchar(10)),并把sex=1的数据修改为FEMALE。

在UserMapper.xml中修改EnumTypeHandler即可按上面的测试方法测试。输出结果:女





    
        
        
        
        
        
        
        
        
    

    
2.2.3自定义枚举typeHandler

MyBatis内部提供的两种转换的typeHandler有很大的局限性,更多时候我们希望使用自定义的typeHandler。

把数据库的sex字段改为整数型,sex=1为男性,sex=0为女性。

@MappedTypes(SexEnum.class)
@MappedJdbcTypes(JdbcType.INTEGER)
public class SexEnumTypeHandler implements TypeHandler{

    @Override
    public void setParameter(PreparedStatement ps, int i, SexEnum parameter,
                             JdbcType jdbcType) throws SQLException {
        ps.setInt(i, parameter.getId());
    }

    @Override
    public SexEnum getResult(ResultSet rs, String columnName)
            throws SQLException {
        int id = rs.getInt(columnName);
        return SexEnum.getSexById(id);
    }

    @Override
    public SexEnum getResult(ResultSet rs, int columnIndex) throws SQLException {
        int id = rs.getInt(columnIndex);
        return SexEnum.getSexById(id);
    }

    @Override
    public SexEnum getResult(CallableStatement cs, int columnIndex)
            throws SQLException {
        int id = cs.getInt(columnIndex);
        return SexEnum.getSexById(id);
    }
}
修改UserMapper.xml文件




    
        
        
        
        
        
        
        
        
        
    

    
2.3typeHandler文件操作

MyBatis对数据库的Blob字段也进行了支持,它提供了一个BlobTypeHandler,为了应付更多场景,它还提供了ByteArrayTypeHandler,只是它不太常用。这里只介绍BlobTypeHandler的用法。

(1)数据库建表:

CREATE TABLE t_file(
	id INT(12) NOT NULL AUTO_INCREMENT,
	content BLOB NOT NULL,
	PRIMARY KEY(id)
);
(2)创建文件POJO
public class TestFile {
    long id;
    byte[] content;

    /** setter and getter **/
}
(3)FileMapper.xml



    
        
        
    

    
注意:

    1.实际上,不加入中typeHandler的属性,MyBatis也能检测的到,并使用合适的typeHandler进行转换。

    2.现实中,一次性地将大量数据加载到JVM中,会给服务器造成很大的压力,所以在更多时候,应考虑使用文件流的形式。这时只要把content的属性改为InputStream即可,如果没有typeHandler声明,系统就会探测并使用BlobInputStreamTypeHandler为你转换结果,就需要将FileMapper.xml中typeHandler修改为org.apache.ibatis.type.BlobInputStreamTypeHandler。

    3.文件的操作在大型互联网的网站上不常用,一般采用文件服务器到的形式,通过更为高速的文件系统操作。


五、(对象工厂)

在默认情况下,MyBatis会使用其定义的对象工厂--DefaultObjectFactory(org.apache.ibatis.reflection.factory.DafaultObjectFactory)来完成对应的工作。

(1)自定义ObjectFactory

在这个类中MyBatis创建了一个List对象和一个Role对象,它先调用方法1,然后调用方法2,只是最后生成了同一个对象,所以在写入的判断中,始终写入的是true。因为返回的是一个Role对象,所以它最后适配为一个Role对象,这就是它的工作过程。

public class MyObjectFactory extends DefaultObjectFactory {

    private static final long serialVersionUID = -2368068322576151574L;

    Logger log = Logger.getLogger(MyObjectFactory.class);

    private Object temp = null;

    @Override
    public void setProperties(Properties properties){
        super.setProperties(properties);
        log.info("初始化参数:【" + properties.toString() +"】");
    }

    //方法2
    @Override
    public  T create(Class type){
        T result = super.create(type);
        log.info("创建对象:" + result.toString());
        log.info("是否和上次创建的是同一对象:【" + (temp == result) + "】");
        return result;
    }

    //方法1
    @Override
    public  T create(Class type, List> constructorArgType, List constructorArgs){
        T result = super.create(type, constructorArgType, constructorArgs);
        log.info("创建对象:" + result.toString());
        temp = result;
        return result;
    }

    @Override
    public  boolean isCollection(Class type){
        return super.isCollection(type);
    }
}(2)配置MyObjectFactory(mybatis-config.xml中) 
  


    
(3)测试

private static void testObjectFactory() {
    Logger log = Logger.getLogger(Main.class);
    SqlSession sqlSession = null;
    try {
        sqlSession = SqlSessionFactoryUtil.openSqlSession();
        UserDao userDao = sqlSession.getMapper(UserDao.class);
        User user = userDao.getUser(1L);
        System.out.println(user.getSex().getName());
    } finally {
        if (sqlSession != null) {
            sqlSession.close();
        }
    }
}

六、(插件)

plugins是MyBatis中最强大和灵活的组件,同时也是最复杂、最难用的组件,而且它十分危险,因为它将覆盖MyBatis底层对象的核心方法和属性。必须清楚掌握MyBatis底层的架构和运行原理,在来研究它。


七、(运行环境)

运行环境的主要作用是配置数据库信息,但实际我们会把这个事情交给Spring。

这里用到了两个元素:transactionManager事务管理器、environment数据源。



    
        
        
        
        
            
            
            
            
        
    
1.transactionManager事务管理器

常见用法有一下三种:




    

①JDBC使用JdbcTransactionFactory生成的JdbcTransaction对象实现。以JDBC的方式对数据库的提交和回滚进行操作的。

②MANAGED使用ManagedTransactionFactory生成的ManagedTransaction对象实现。它的提交和回滚不用任何操作,而是把事务交给容器处理。在默认情况下,它会关闭连接,然而一些容器不希望这样,因此需要将closeConnection属性设置为false来阻止它默认的关闭行为

③自定义事务管理器。

   自定义事务工厂:

public class MyObjectFactory extends DefaultObjectFactory {

    private static final long serialVersionUID = -2368068322576151574L;

    Logger log = Logger.getLogger(MyObjectFactory.class);

    private Object temp = null;

    @Override
    public void setProperties(Properties properties){
        super.setProperties(properties);
        log.info("初始化参数:【" + properties.toString() +"】");
    }

    //方法2
    @Override
    public  T create(Class type){
        T result = super.create(type);
        log.info("创建对象:" + result.toString());
        log.info("是否和上次创建的是同一对象:【" + (temp == result) + "】");
        return result;
    }

    //方法1
    @Override
    public  T create(Class type, List> constructorArgType, List constructorArgs){
        T result = super.create(type, constructorArgType, constructorArgs);
        log.info("创建对象:" + result.toString());
        temp = result;
        return result;
    }

    @Override
    public  boolean isCollection(Class type){
        return super.isCollection(type);
    }
}   自定义事务类: 
  
public class MyTransaction extends JdbcTransaction implements Transaction {

    public MyTransaction(DataSource ds, TransactionIsolationLevel desiredLevel, boolean desiredAutoCommit) {
        super(ds, desiredLevel, desiredAutoCommit);
    }

    public MyTransaction(Connection connection) {
        super(connection);
    }

    @Override
    public Connection getConnection() throws SQLException{
        return super.getConnection();
    }

    @Override
    public void commit() throws SQLException {
       super.commit();
    }

    @Override
    public void rollback() throws SQLException {
        super.rollback();
    }

    @Override
    public void close() throws SQLException {
       super.close();
    }

    @Override
    public Integer getTimeout() throws SQLException {
        return super.getTimeout();
    }
}

2.environment数据源环境

数据源有三种,当然也可以自定义数据源。



在MyBatis中,数据库通过PooledDataSourceFactory、UnpooledDataSourceFactory和JndiDataSourceFactory三个工厂类来提供,前两者对应产生PooledDataSource、UnpooledDataSource类对象,而JndiDataSourceFactory则会根据JNDI的信息拿到外部容器实现的数据库连接对象。无论如何,这三个工厂类最后生成的产品都会是一个实现了DataSource接口的数据库连接对象。

自定义数据源工厂,如DBCP数据源:

public class DbcpDataSourceFactory implements DataSourceFactory{

    private Properties props = null;

    @Override
    public void setProperties(Properties properties) {
        this.props = properties;
    }

    @Override
    public DataSource getDataSource() {
        DataSource dataSource = null;
        try {
            dataSource = BasicDataSourceFactory.createDataSource(props);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return dataSource;
    }
}

mybatis-config.xml配置:



    
    
    
    

八、(数据库厂商标识)

1.系统默认配置相关:


    
    
    

解析:property元素的属性name是数据库的名称,如果不会填写,可用JDBC创建其数据库连接对象connection,然后connection.getMetaData().getDatabaseProductName()获取。而属性value是它的一个别名,在MyBatis里可以通过这个别名标识一条SQL适用于哪种数据库运行。


注意:①多数据库SQL时需要配置databaseIdProvidertype的属性。

系统优先取到和数据库配置一致的SQL;如果没有,则取没有databaseId的属性,可以把它当作默认值;如果还是取不到则会抛出异常


2.databaseIdProvider也可以自定义

实现接口DatabaseIdProvider

public class MyDatabaseIdProvider implements DatabaseIdProvider {
    private static final String DATEBASE_TYPE_DB2 = "DB2";
    private static final String DATEBASE_TYPE_MYSQL = "MySQL";
    private static final String DATEBASE_TYPE_ORACLE = "Oralce";

    private Logger log = Logger.getLogger(MyDatabaseIdProvider.class);

    @Override
    public void setProperties(Properties props) {
        log.info(props);
    }

    @Override
    public String getDatabaseId(DataSource dataSource) throws SQLException {
        Connection connection = dataSource.getConnection();
        String dbProductName = connection.getMetaData().getDatabaseProductName();
        if (MyDatabaseIdProvider.DATEBASE_TYPE_DB2.equals(dbProductName)) {
            return "db2";
        } else if (MyDatabaseIdProvider.DATEBASE_TYPE_MYSQL
                .equals(dbProductName)) {
            return "mysql";
        } else if (MyDatabaseIdProvider.DATEBASE_TYPE_ORACLE
                .equals(dbProductName)) {
            return "oracle";
        } else {
            return null;
        }
    }
}
mybatis-config.xml中配置:

    

九、(引入映射器)

定义映射器




    
引入映射器的方法:

①文件路径引入


    

②包名引入


    

③类注册引入


    

④用userMapper.xml引入


    










你可能感兴趣的:(⊹●mybatis)