SqlMapConifg.xml
(可叫别的):
MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息
properties(属性)
settings(设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境配置)
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
databaseIdProvider(数据库厂商标识)
mappers(映射器)
MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中, 现实情况下有多种理由需要这么做。
开发、测试和生产环境需要有不同的配置
想在具有相同 Schema 的多个生产数据库中 使用相同的 SQL 映射
不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。
<environments default="mysql">
<environment id="mysql">
<transactionManager type="jdbc"/>
<dataSource type="pooled">
<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>
<environment id="development">
<transactionManager type="jdbc">
<property name="..." value="..."/>
transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
dataSource>
environment>
environments>
environments
的default
属性配置了默认选择的环境<properties resource="jdbcConfig.properties">
<property name="aaa" value="bbb"/>
<property name="ccc" value="ddd"/>
properties>
properties
标签的resource
属性来引入外部配置文件*.properties
properties
标签内添加子标签peoperty
设置属性键值对类型别名是为 Java 类型设置一个短的名字
它只和 XML 配置有关,存在的意义仅在于用来减少类完全限定名的冗余。例如:
<typeAliases>
<typeAlias type="com.ajacker.domain.User" alias="user"/>
typeAliases>
这时我们就可以把之前配置的类名使用类型别名代替:
<select id="findAll" resultType="user">
select * from user
select>
也可以使用package
标签指定一个包,Mybatis
会在包下扫描类,并用首字母小写的类名作为默认别名
<typeAliases>
<package name="com.ajacker.domain"/>
typeAliases>
这时我们的别名就变为了user
,达到了同样的效果
这时可以通过注解@Alias()
手动设置其它的别名:
@Alias("userAccount")
public class User implements Serializable {
...
}
此时的别名为“userAccount”
这里只说常用的设置,具体的可以查官方文档
设置名 | 描述 | 有效值 | 默认值 |
---|---|---|---|
useGeneratedKeys | 允许 JDBC 支持自动生成主键,需要驱动支持。 如果设置为 true 则这个设置强制使用自动生成主键,尽管一些驱动不能支持但仍可正常工作(比如 Derby)。 | true | false | False |
cacheEnabled | 全局地开启或关闭配置文件中的所有映射器已经配置的任何缓存。 | true | false | true |
lazyLoadingEnabled | 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。 |
true | false | false |
mapUnderscoreToCamelCase | 是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN 到经典 Java 属性名 aColumn 的类似映射。 | true | false | False |
<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>
可以使用以下四种方式配置映射器:
<mappers>
<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
<mapper resource="org/mybatis/builder/BlogMapper.xml"/>
<mapper resource="org/mybatis/builder/PostMapper.xml"/>
mappers>
<mappers>
<mapper url="file:///var/mappers/AuthorMapper.xml"/>
<mapper url="file:///var/mappers/BlogMapper.xml"/>
<mapper url="file:///var/mappers/PostMapper.xml"/>
mappers>
<mappers>
<mapper class="org.mybatis.builder.AuthorMapper"/>
<mapper class="org.mybatis.builder.BlogMapper"/>
<mapper class="org.mybatis.builder.PostMapper"/>
mappers>
<mappers>
<package name="org.mybatis.builder"/>
mappers>
resources
下相同的路径下SqlSessionFactory
,就不再需要它了SqlSessionFactory
一旦被创建就应该在应用的运行期间一直存在SqlSession
实例SqlSession
的实例不是线程安全的,因此是不能被共享的SqlSession
关闭的标准模式:try (SqlSession session = sqlSessionFactory.openSession()) {
// 你的应用逻辑代码
}
在你的所有的代码中一致地使用这种模式来保证所有数据库资源都能被正确地关闭
我们之前要求数据库的列名和实体类的属性名保持一致,因为Mybatis
会自动把名字相同进行映射,可是当我们遇到列名和属性名不同的时候,该如何处理呢?
我们将之前的例子中的id
属性替换为userId
,此时属性名和数据库中的列名不对应
我们通过设置结果集映射,可以手动指定列名和实体类属性名的对应关系,此时在sql语句配置中,应将resultType
属性替换为resultMap
,并引用结果集id,例如:
<resultMap id="userMap" type="user">
<result property="userId" column="id"/>
resultMap>
<select id="findAll" resultMap="userMap">
select * from user
select>
我们修改sql语句,设置查询返回列名的别名,例如下面将id
列的别名设置为userId
,达到和属性名匹配的目的:
<select id="findAll" resultMap="userMap">
select id as userId, username, birthday, sex, address from user
select>
如果数据库出现错误,我们可以通过日志了解更详细的信息,便于排错。
我们可以通过设置属性LogImpl
来指定日志工厂:
导入包
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<version>1.2.17version>
dependency>
设置配置文件
# 控制日志输出的位置
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=\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
设置日志工厂
<setting name="logImpl" value="LOG4J"/>
简单使用
导入包并初始化Logger对象
private static Logger logger = Logger.getLogger(MyBatisTest.class);
在合适的位置添加log语句
@After
public void destroy() throws IOException {
//提交事务
sqlSession.commit();
logger.info("SqlSession提交事务");
//6.释放资源
sqlSession.close();
in.close();
logger.info("释放资源");
}
查看效果
为什么要分页?
语法
select * from user limit startIndex,pageSize
编写接口
/**
* 有限制的查询所有
* @param startIndex
* @param limit
* @return
*/
List<User> findAllByLimit(@Param("startIndex") int startIndex, @Param("limit") int limit);
编写mapper中对应的sql语句:
<select id="findAllByLimit" resultMap="userMap">
select * from user limit #{startIndex},#{limit}
select>
编写测试类测试:
@Test
public void testFindAllByLimit(){
List<User> userList = userDao.findAllByLimit(0, 4);
userList.forEach(System.out::println);
}
不使用sql使用代码的形式实现分页
使用一般的接口:(sql语句中没有limit)
/**
* 查询所有
* @return 查询结果
*/
List<User> findAll();
mapper中对应的sql语句为:
<select id="findAll" resultMap="userMap">
select * from user
select>
在调用处使用RowBounds
@Test
public void testFindAllByRowBounds(){
//rowBounds可以设置startIndex和pageSize
RowBounds rowBounds = new RowBounds(0,4);
List<User> userList = sqlSession.selectList("com.ajacker.dao.IUserDao.findAll", null,rowBounds);
userList.forEach(System.out::println);
}
我们把之前所有的方法都用注解实现一次,删除xml的mapper文件
/**
* @author ajacker
* 用户的持久层接口
*/
public interface IUserDao {
@Results(id = "userMap", value = {
@Result(property = "userId", column = "id")
})
/**
* 查询所有
* @return 查询结果
*/
@Select({"select * from user"})
@ResultMap("userMap")
List<User> findAll();
/**
* 保存用户
* @param user 要保存的用户
*/
@Insert("insert into user(username, address, sex, birthday) values(#{username},#{address},#{sex},#{birthday})")
@SelectKey(statement = "select last_insert_id()",
keyProperty = "userId", keyColumn = "id",
before = false,
resultType = Integer.class)
void saveUser(User user);
/**
* 插入用户,参数用map包装
* @param map
*/
@Insert("insert into user(username, address, sex, birthday) values(#{userId},#{userAds},#{userSex},#{userBirth})")
void saveUserUseMap(Map<String, Object> map);
/**
* 更新用户
* @param user
*/
@Update("update user set username=#{username},address=#{address},sex=#{sex},birthday=#{birthday} where id=#{id}")
void updateUser(User user);
/**
* 根据id删除用户
* @param userId
*/
@Delete("delete from user where id=#{uid}")
void deleteUser(int userId);
/**
* 根据id查询用户
* @param userId
* @return
*/
@Select("select * from user where id=#{uid}")
@ResultMap("userMap")
User findById(int userId);
/**
* 根据名字模糊查询
* @param name
* @return
*/
@Select("select * from user where username like #{uname}")
@ResultMap("userMap")
List<User> findByName(String name);
/**
* 查询总用户数
* @return
*/
@Select("select count(id) from user")
int findTotal();
/**
* 根据QueryVo中的条件模糊查询
* @param vo
* @return
*/
@Select("select * from user where username like #{user.username}")
@ResultMap("userMap")
List<User> findUserByVo(QueryVo vo);
/**
* 有限制的查询所有
* @param startIndex
* @param limit
* @return
*/
@Select("select * from user limit #{startIndex},#{limit}")
@ResultMap("userMap")
List<User> findAllByLimit(@Param("startIndex") int startIndex, @Param("limit") int limit);
}
类型:
@Select
:和select
标签对应@Delete
:和delete
标签对应@Update
:和update
标签对应@Insert
:和insert
标签对应@Results
注解,这和我们的ResultMap
标签对应@Results
的value
属性中填写多个@Result
注解,这和result
标签对应@Results(id = "userMap", value = {
@Result(property = "userId", column = "id")
})
相当于:
<resultMap id="userMap" type="user">
<result property="userId" column="id"/>
resultMap>
ResultMap
之后可以通过在方法上添加@ResultMap
注解来设置@SelectKey
,和selectKey
标签对应,before = false
,代表设置为after@SelectKey(statement = "select last_insert_id()",
keyProperty = "userId", keyColumn = "id",
before = false,
resultType = Integer.class)
相当于:
<selectKey keyProperty="id" keyColumn="id" resultType="int" order="AFTER">
select last_insert_id()
selectKey>