Mybatis配置
mybatis配置文件并不复杂,它的所有元素如下:
<?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/><!-- 属性 --> <settings/><!-- 设置 --> <typealiases/><!-- 类型别名 --> <typehandlers/><!-- 类型处理器 --> <objectfactory/><!-- 对象工厂 --> <plugins/><!-- 插件 --> <environments><!-- 配置环境 --> <environment><!-- 环境变量 --> <transactionmanager/><!-- 事务管理器 --> <datasource/><!-- 数据源 --> </environment> </environments> <databaseidprovider/><!-- 数据库厂商标识 --> <mappers/><!-- 映射器 --> </configuration>
需要注意的是,顺序不能颠倒,否则启动会发生异常.
1、properties属性
properties属性可以给系统配置一些运行参数,可以放在xml文件或properties文件中。mybatis提供了三种方式让我们使用properties:
property元素;
properties文件;
程序代码传递;
1.1 property元素:
<?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> <property name="db.driver" value="com.mysql.jdbc.driver"/> <property name="db.url" value="jdbc:mysql://localhost:3306/ssm"/> <property name="db.username" value="root"/> <property name="db.password" value="llll"/> </properties> <environments default="development"> <environment id="development"> <transactionmanager type="jdbc"></transactionmanager> <datasource type="pooled"> <property name="driver" value="${db.driver}"/> <property name="url" value="${db.url}"/> <property name="username" value="${db.username}"/> <property name="password" value="${db.password}"/> </datasource> </environment> </environments> </configuration>
这样就可以一次定义到处使用了
1.2 使用properties文件
这个是比较普遍的大家常用的方法。一来这个文件十分简单,里面就是存的键值对,二来可以吧多个键值对放在不同文件,以便于日后维护。
jdbc.properties如下:
jdbc.driver=com.mysql.jdbc.driver jdbc.url=jdbc:mysql://localhost:3306/ssm jdbc.username=root jdbc.password=qq11qq
在mybatis中通过properties的属性resource来引入properties文件
<properties resource="jdbc.properties" />
1.3 使用程序传递方式传递参数
在真实的生产环境中,数据库密码需要保密。故一般都需要将用户名密码经过加密成为密文后,配置到properties文件中。假设提供了utils.decode(cipfertext)
进行解密。那么创建sqlsessionfactory代码清单如下所示:
string resource = "mybatis-config.xml"; inputstream inputstream; inputstream in = resources.getresourceasstream(resource); properties props = new properties(); props.load(in); string username = props.getproperty("jdbc.username"); string password = props.getproperty("jdbc.password"); props.put("jdbc.username",utils.decode(username)); props.put("jdbc.password",utils.decode(password)); inputstream = resources.getresourceasstream(resource); sqlsessionfactory sqlsessionfactory = new sqlsessionfactorybuilder().build(inputstream,props);
2、settings设置
settings是mybatis中最复杂的配置,它能深刻的影响mybatis底层的运行,但是在大部分情况下,使用默认值便可以运行。下面是全量的配置案例:
<settings> <setting name="cacheenabled" value="true"/> <setting name="lazyloadingenabled" value="true"/> <setting name="multipleresultsetsenabled" value="true"/> <setting name="usecolumnlable" value="true"/> <setting name="usegeneratedkeys" value="false"/> <setting name="automappingbehavior" value="partial"/> <setting name="automappingunknowcolumnbehavior" value="warnning"/> <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>
常见的几个设置的用法简介:
| 配置项 | 作用 | 配置选项说明 | 默认值 |
| :------: | :------: | :------: | :------: |
| cacheenabled | 该配置影响所有映射器中配置缓存的全局开关 | true/false | true |
|lazyloadingenabled | 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。在特定关联关系中可通过设置fetchtype属性来覆盖该项的开关状态 | true/false | false |
| aggressivelazyloading | 当启用时,对任意延迟属性的调用会使得带有延迟加载属性的对象完整加载;反之,每种属性将会按需加载 | true/false | 版本3.1.4(不包含)之前true,之后false |
| automappingbehavior | 指定mybatis应如何映射列到字段或属性。none表示取消自动映射;partial表示只会自动映射,没有定义嵌套结果集和映射结果集;full会自动映射任意的复杂结果集(无论是否嵌套) | none/partial/full | partial |
|defaultexecutortype | 配置默认的执行器 。simple是普通的执行器;reuse会重用预处理语句(prepared statements);batch执行器将重用语句并执行批量更新 | simple/reuse/batch | simple |
| mapunderscoretocamelcase | 是否开启自动驼峰命名规则映射,即从经典数据库列名a_column到经典java属性名acolumn的类似映射 | true/false | false|
3、typealiases 别名
由于类的全限定名很长,在多次需要使用的时候,比较不方便,在mybatis中允许定义一个简写来代替,这就是别名,分为系统定义和自定义别名。在mybatis中别名由类typealiasregistry来定义(org.apache.ibatis.type.typealiasregistry)。在mybatis中,别名不区分大小写。
3.1、自定义别名
由于现实开发中,存在许多自定义的对象需要重复使用,因此mybatis中也提供了用户自定义别名的规则。我们可以通过typealiasregistry类的registeralias方法注册,也可以采用配置文件或或者扫描方式来定义它。
3.1.1、使用配置文件定义
<typealiases> <typealias type="com.ssm.pojo.role" alias="role"/> <typealias type="com.ssm.pojo.user" alias="user"/> </typealiases>
3.1.2、扫描
现实开发中可能存在大量的实体类,如果一个一个的配置就不适合了。因此mybatis还提供了扫描别名。如上配置:两个实体类位于同一包下,还可以这样配置:
<typealiases> <package name="com.ssm.pojo"/> </typealiases>
3.1.3、注解配置别名
如果多个类同时包含user实体类,在通过包扫描后,可能会有冲突。则可以通过注解来区分。比如:
package com.ssm.pojo; @alias("user3") public class user{ ... ... }
这样就可以避免因为别名重名导致的扫描失败的问题。
4、typehandler类型转换器
在jdbc中,需要在preparedstatement对象中设置哪些已经预编译过的sql语句的参数。执行sql后,会通过resultset对象获取得到数据库的数据。而这些,mybatis是根据数据库数据的类型通过typehandler来实现的。
在mybatis中,typehandler都需要实现接口org.apache.ibatis.type.typehandler
,源码如下:
public interface typehandler <t> { void setparameter(java.sql.preparedstatement preparedstatement, int i, t t, org.apache.ibatis.type.jdbctype jdbctype) throws java.sql.sqlexception; t getresult(java.sql.resultset resultset, java.lang.string s) throws java.sql.sqlexception; t getresult(java.sql.resultset resultset, int i) throws java.sql.sqlexception; t getresult(java.sql.callablestatement callablestatement, int i) throws java.sql.sqlexception; }
其中setparameter方法是使用typehandler通过preparedstatement对象进行设置sql参数的时候使用的具体方法,其中i是参数在sql的下标,parameter是参数,jdbctype是数据库类型。
如果研究typehandler的源码,会发现其都实现了org.apache.ibatis.type.basetypehandler
,所以我们先看一下basetypehandler的源码:
/** * copyright 2009-2019 the original author or authors. * * licensed under the apache license, version 2.0 (the "license"); * you may not use this file except in compliance with the license. * you may obtain a copy of the license at * * http://www.apache.org/licenses/license-2.0 * * unless required by applicable law or agreed to in writing, software * distributed under the license is distributed on an "as is" basis, * without warranties or conditions of any kind, either express or implied. * see the license for the specific language governing permissions and * limitations under the license. */ package org.apache.ibatis.type; import java.sql.callablestatement; import java.sql.preparedstatement; import java.sql.resultset; import java.sql.sqlexception; import org.apache.ibatis.executor.result.resultmapexception; import org.apache.ibatis.session.configuration; /** * the base {@link typehandler} for references a generic type. * <p> * important: since 3.5.0, this class never call the {@link resultset#wasnull()} and * {@link callablestatement#wasnull()} method for handling the sql {@code null} value. * in other words, {@code null} value handling should be performed on subclass. * </p> * * @author clinton begin * @author simone tripodi * @author kzuki shimizu */ public abstract class basetypehandler<t> extends typereference<t> implements typehandler<t> { /** * @deprecated since 3.5.0 - see https://github.com/mybatis/mybatis-3/issues/1203. this field will remove future. */ @deprecated protected configuration configuration; /** * @deprecated since 3.5.0 - see https://github.com/mybatis/mybatis-3/issues/1203. this property will remove future. */ @deprecated public void setconfiguration(configuration c) { this.configuration = c; } @override public void setparameter(preparedstatement ps, int i, t parameter, jdbctype jdbctype) throws sqlexception { if (parameter == null) { if (jdbctype == null) { throw new typeexception("jdbc requires that the jdbctype must be specified for all nullable parameters."); } try { ps.setnull(i, jdbctype.type_code); } catch (sqlexception e) { throw new typeexception("error setting null for parameter #" + i + " with jdbctype " + jdbctype + " . " + "try setting a different jdbctype for this parameter or a different jdbctypefornull configuration property. " + "cause: " + e, e); } } else { try { setnonnullparameter(ps, i, parameter, jdbctype); } catch (exception e) { throw new typeexception("error setting non null for parameter #" + i + " with jdbctype " + jdbctype + " . " + "try setting a different jdbctype for this parameter or a different configuration property. " + "cause: " + e, e); } } } @override public t getresult(resultset rs, string columnname) throws sqlexception { try { return getnullableresult(rs, columnname); } catch (exception e) { throw new resultmapexception("error attempting to get column '" + columnname + "' from result set. cause: " + e, e); } } @override public t getresult(resultset rs, int columnindex) throws sqlexception { try { return getnullableresult(rs, columnindex); } catch (exception e) { throw new resultmapexception("error attempting to get column #" + columnindex + " from result set. cause: " + e, e); } } @override public t getresult(callablestatement cs, int columnindex) throws sqlexception { try { return getnullableresult(cs, columnindex); } catch (exception e) { throw new resultmapexception("error attempting to get column #" + columnindex + " from callable statement. cause: " + e, e); } } public abstract void setnonnullparameter(preparedstatement ps, int i, t parameter, jdbctype jdbctype) throws sqlexception; /** * @param columnname colunm name, when configuration <code>usecolumnlabel</code> is <code>false</code> */ public abstract t getnullableresult(resultset rs, string columnname) throws sqlexception; public abstract t getnullableresult(resultset rs, int columnindex) throws sqlexception; public abstract t getnullableresult(callablestatement cs, int columnindex) throws sqlexception; }
我们简单分析一下源码:
1、这是个抽象类,实现了typehandler接口的四个方法,定义了4个抽象方法,需要其子类去实现。下面将会以stringtypehandler来简单说明一下。
2、getresult方法:非空结果集是通过getnullableresult来实现。
3、setparameter方法:如果parameter与jdbctype同时为null,抛出异常。
4、getnullableresult用于存储过程。
下面通过stringtypehandler 来加深了解typehandler:
/** * copyright 2009-2015 the original author or authors. * * licensed under the apache license, version 2.0 (the "license"); * you may not use this file except in compliance with the license. * you may obtain a copy of the license at * * http://www.apache.org/licenses/license-2.0 * * unless required by applicable law or agreed to in writing, software * distributed under the license is distributed on an "as is" basis, * without warranties or conditions of any kind, either express or implied. * see the license for the specific language governing permissions and * limitations under the license. */ package org.apache.ibatis.type; import java.sql.callablestatement; import java.sql.preparedstatement; import java.sql.resultset; import java.sql.sqlexception; /** * @author clinton begin */ public class stringtypehandler extends basetypehandler<string> { @override public void setnonnullparameter(preparedstatement ps, int i, string parameter, jdbctype jdbctype) throws sqlexception { ps.setstring(i, parameter); } @override public string getnullableresult(resultset rs, string columnname) throws sqlexception { return rs.getstring(columnname); } @override public string getnullableresult(resultset rs, int columnindex) throws sqlexception { return rs.getstring(columnindex); } @override public string getnullableresult(callablestatement cs, int columnindex) throws sqlexception { return cs.getstring(columnindex); } }
在mybatis中采用org.apache.ibatis.type.typehandlerregistry类对象的register来进行注册。其源码如下(已删减部分)
/** * @author clinton begin * @author kazuki shimizu */ public final class typehandlerregistry { public typehandlerregistry() { register(boolean.class, new booleantypehandler()); register(boolean.class, new booleantypehandler()); ... ... register(byte[].class, new byteobjectarraytypehandler()); register(byte[].class, jdbctype.blob, new blobbyteobjectarraytypehandler()); ... ... } }
自定义的typehandler一般是通过配置或扫描:
根据系统定义的可以知道,要实现typehandler就需要去实现接口typehandler或者继承basetypehandler(其也实现了接口typehandler)。这里我们仿造一个stringtypehandler。代码如下:
package com.ssm.chapter3.typehandler; import org.apache.ibatis.type.jdbctype; import org.apache.ibatis.type.typehandler; import org.apache.log4j.logger; import java.sql.callablestatement; import java.sql.preparedstatement; import java.sql.resultset; import java.sql.sqlexception; /** * created by czz on 2019/8/4. */ public class mytypehandler implements typehandler<string> { logger log = logger.getlogger(mytypehandler.class); @override public void setparameter(preparedstatement ps, int i, string parameter, jdbctype jdbctype) throws sqlexception{ log.info("string参数为: 【"+parameter+"】"); ps.setstring(i,parameter); } @override public string getresult(resultset rs, string columnname) throws sqlexception{ string result = rs.getstring(columnname); log.info("读取string参数-01 : 【"+result+"】"); return result; } @override public string getresult(resultset rs, int columnindex) throws sqlexception{ string result = rs.getstring(columnindex); log.info("读取string参数-02 : 【"+result+"】"); return result; } @override public string getresult(callablestatement cs, int columnindex) throws sqlexception{ string result = cs.getstring(columnindex); log.info("读取string参数-03 : 【"+result+"】"); return result; } }
定义完成之后,还需要额皮质完成系统才会读取它,注册它。
<typehandlers> <typehandler handler="com.ssm.chapter3.typehandler.mytypehandler" jdbctype="varchar" javatype="string"/> </typehandlers>
还可以显示启用typehandler,代码如下:
<resultmap id="rolemapper" type="role"> <result property="id" column="id"></result> <result property="rolename" column="role_name" jdbctype="varchar" javatype="string"/> <result property="note" column="note" typehandler="com.ssm.chapter3.typehandler.mytypehandler"/> </resultmap> <select id="findroles" parametertype="long" resulttype="com.ssm.pojo.role"> select id ,role_name rolename,note from t_role where role_name like concat('%',#{rolename,jdbctype=varchar,javatype=string},'%') </select> <select id="findroles" parametertype="long" resulttype="com.ssm.pojo.role"> select id ,role_name rolename,note from t_role where role_name like concat('%',#{rolename,typehandler=com.ssm.chapter3.typehandler.mytypehandler},'%') </select>
由于有时候自定义的类型会比较多,可以考虑使用包扫描的形式。但是这样就无法指定javatype和jdbctype了,此时可以用注解处理。代码如下:
<typehandlers> <package name="com.ssm.chapter3.typehandler"/> </typehandlers>
@mappedtypes(string.class) @mappedjdbctypes(jdbctype.varchar) public class mytypehandler implements typehandler<string> { ... ... }
5、objectfactory(对象工厂)
当创建结果集时,mybatis会使用一个对象工厂来创建这个结果集实例。在默认的情况下,mybatis会使用其自定义的对象工厂------defaultobjectfactory(org.apache.ibatis.reflection.defaultobjectfactory),并给予配置。在大部分情况下,我们都不需要自定义返回规则,因为这些比较复杂且容易出错。更多的情况,可能是继承defaultobjectfactory,通过一定的改写来完成我们所需要的工作。
6、插件
插件是mybatis中最强大和灵活的组件,同时也是最复杂最难以使用的的组件,而且因为它将覆盖mybatis底层对象的核心方法和属性,所以十分危险。
7、environments(运行环境)
在mybatis中,运行环境的主要作用是配置数据库信息,它可以配置多个数据库,一般而言只需要配置其中的一个就可以了。他下面又分为两个可配置的元素:事务管理器(transactionmanager)和数据源(datasource)。运行环境配置,代码如下:
<environments default="development"> <environment id="development"> <transactionmanager type="jdbc"></transactionmanager> <datasource type="pooled"> <property name="driver" value="${db.driver}"/> <property name="url" value="${db.url}"/> <property name="username" value="${db.username}"/> <property name="password" value="${db.password}"/> </datasource> </environment> </environments>
7.1、事务管理器(transactionmanager)
在mybatis中transactionmanager提供了两个实现类,他们需要实现接口org.apache.ibatis.transaction.transaction
接口定义如下:
/** * copyright 2009-2019 the original author or authors. * * licensed under the apache license, version 2.0 (the "license"); * you may not use this file except in compliance with the license. * you may obtain a copy of the license at * * http://www.apache.org/licenses/license-2.0 * * unless required by applicable law or agreed to in writing, software * distributed under the license is distributed on an "as is" basis, * without warranties or conditions of any kind, either express or implied. * see the license for the specific language governing permissions and * limitations under the license. */ package org.apache.ibatis.transaction; import java.sql.connection; import java.sql.sqlexception; public interface transaction { connection getconnection() throws sqlexception; void commit() throws sqlexception; void rollback() throws sqlexception; void close() throws sqlexception; integer gettimeout() throws sqlexception; }
根据接口的方法可知,它主要的工作就是提交(commit),回滚(rollback)和关闭(close)数据库的事务。mybatis为transaction接口提供了两个实现类jdbctransaction和managedtransaction,因此对应着两个工厂jdbctransactionfactory和managedtransactionfactory。这两个工厂都需要实现transactionfactory接口,通过他们会生成transaction对象。于是,可以把事务管理器配置成一下两种方式:
<transactionmanager type="jdbc"></transactionmanager> <transactionmanager type="manaed"></transactionmanager>
transactionfactory源码如下:
public interface transactionfactory { default void setproperties(properties props) { // nop } transaction newtransaction(connection conn); transaction newtransaction(datasource datasource, transactionisolationlevel level, boolean autocommit); }
下面是jdbctransactionfactory 的源码,通过源码可以更了解该工厂如何生产jdbctransaction的。这部分源码相对来讲比较容易理解。
/** * copyright 2009-2019 the original author or authors. * * licensed under the apache license, version 2.0 (the "license"); * you may not use this file except in compliance with the license. * you may obtain a copy of the license at * * http://www.apache.org/licenses/license-2.0 * * unless required by applicable law or agreed to in writing, software * distributed under the license is distributed on an "as is" basis, * without warranties or conditions of any kind, either express or implied. * see the license for the specific language governing permissions and * limitations under the license. */ package org.apache.ibatis.transaction.jdbc; import java.sql.connection; import javax.sql.datasource; import org.apache.ibatis.session.transactionisolationlevel; import org.apache.ibatis.transaction.transaction; import org.apache.ibatis.transaction.transactionfactory; public class jdbctransactionfactory implements transactionfactory { @override public transaction newtransaction(connection conn) { return new jdbctransaction(conn); } @override public transaction newtransaction(datasource ds, transactionisolationlevel level, boolean autocommit) { return new jdbctransaction(ds, level, autocommit); } }
当然如果不想采用mybatis给提供的规则,我们可以自定义事务工厂:
package com.ssm.chapter3.objectfactory; import org.apache.ibatis.session.transactionisolationlevel; import org.apache.ibatis.transaction.transaction; import org.apache.ibatis.transaction.jdbc.jdbctransaction; import javax.sql.datasource; import java.sql.connection; import java.sql.sqlexception; /** * created by czz on 2019/8/5. */ public class mytransaction extends jdbctransaction implements transaction { public mytransaction(datasource datasource, transactionisolationlevel level,boolean autocommit){ super(datasource,level,autocommit); } public mytransaction(connection conn){ super(conn); } @override public connection getconnection() throws sqlexception { return super.getconnection(); } @override public void rollback() throws sqlexception{ super.rollback(); } @override public void commit() throws sqlexception{ super.commit(); } @override public integer gettimeout() throws sqlexception{ return super.gettimeout(); } }
package com.ssm.chapter3.objectfactory; import org.apache.ibatis.session.transactionisolationlevel; import org.apache.ibatis.transaction.transaction; import org.apache.ibatis.transaction.transactionfactory; import javax.sql.datasource; import java.sql.connection; import java.util.properties; /** * created by czz on 2019/8/5. */ public class mytransactionfactory implements transactionfactory { @override public void setproperties(properties props){} @override public transaction newtransaction(connection conn){ return new mytransaction(conn); } @override public transaction newtransaction(datasource datasource, transactionisolationlevel level,boolean autocommit){ return new mytransaction(datasource,level,autocommit); } }
配置自定义事务
<transactionmanager> type="com.learn.ssm.chapter4.transaction,mytransactionfactory"
7.2environment数据源环境
environment的主要作用是配置数据库,在mybatis中,数据库通过pooleddatasourcefactory、unpooleddatasourcefactory、jndidatasourcefactory三个工厂类来提供。前两者会产生pooleddatasource、unpooleddatasource对象,而jndidatasourcefactory则会jndi的信息拿到外部容器实现的数据库连接对象。无论如何,这三个工厂类生成的产品都是一个实现了datasource接口的数据库连接对象。配置方法分别如下所示:<datasource type="unpooled">
<datasource type="pooled">
<datasource type="jndi">
7.2.1 unpooled
采用非数据库池的管理方式,每次请求都会打开一个新的数据库连接,所以创建会比较慢。在一些对性能要求没那么高的场合可以使用。可以配置以下属性:
driver:数据库驱动名;
url
username
password
defaulttransactionisolationlevel:默认的连接事务隔离级别。
7.2.2 pooled
利用池的概念将jdbc的connection对象组织起来。它开始会有一些空置,以及一些连接好的数据库连接。所以在请求时,无需再建立和验证,省去了创建新的连接实例时所必需的初始化和认证时间。它还空置最大连接数,以免过多的连接导致系统瓶颈。
除了unpooled的属性外,会有更多属性用来配置pooled数据源:
poolmaximumactiveconnections:
poolmaximumidleconnections:
poolmaximumcheckouttime:
pooltimetowait:
poolpingquery:
poolpingenabled:
poolpingconnectionsnotuserfor:
7.2.3 jndi
可以自定义数据源工厂,并进行配置
<datasource type="com.......dbcpdatasourcefactory"> <property name="driver" value=""/> ... ... </datasource>
8、databaseidprovider数据库厂商标识
主要用于给不同客户提供系统需要不同数据库连接时,配置如下:
<databaseidprovider type="db_vendor"> <property name="oracle" value="oracle"/> <property name="mysql" value="mysql"/> <property name="db2" value="db2"/> </databaseidprovider>
标识sql适用何种数据库:
<select id="getrole" parametertype="long" resulttype="role" databaseid="oracle"> select id,role_name as rolename ,note from t_role where id = #{id} </select>
<select id="getrole" parametertype="long" resulttype="role" databaseid="mysql"> select id,role_name as rolename ,note from t_role where 1=1 and id = #{id} </select>
9、引入映射器的方法
<!--文件路径引入 --> <mappers> <mapper resource="com/ssm/mapper"/> </mappers> <!--包名引入 --> <mappers> <package name="com.ssm.mapper"/> </mappers> <!--类注册引入 --> <mappers> <mapper class="com.ssm.mapper.rolemapper"/> <mapper class="com.ssm.mapper.usermapper"/> </mappers> <!--使用usermapper.xml引入 --> <mappers> <mapper url="file:///var/mappers/com/ssm/mapper/usermapper.xml"/> <mapper url="file:///var/mappers/com/ssm/mapper/rolemapper.xml"/> </mappers>
大家有兴趣也可以关注我的公众号查看文章。
推荐阅读
-
mysql 5.7以上版本安装配置方法图文教程(mysql 5.7.12mysql 5.7.13mysql 5.7.14)
-
Eclipse怎么在线配置Hibernate Tools?
-
eclipse怎么删除配置好的多余的工作空间?
-
Mysql5.7.17 winx64.zip解压缩版安装配置图文教程
-
MySQL 5.6下table_open_cache参数优化合理配置详解
-
Centos7安装和配置Mysql5.7
-
eclipse怎么创建servlet中class并配置servlet教程
-
Android studio怎么配置代码检查规则?
-
Visual Studio怎么重新配置开发环境?
-
mybatis查询语句的背后之封装数据