Mybatis使用和原理
- 1、ORM架构
- 2、Spring整合MyBatis使用
- 2.1 添加maven依赖
- 2.2 配置数据源
- 2.3 创建实体类
- 2.4 创建 MyBatis Mapper
- 2.4.1 使用MyBatis注解
- 2.4.2 使用XML方式
- 2.5 Service 层
- 3、Spring整合Hibernate使用
- 3.1 添加maven依赖
- 3.2 配置数据源
- 3.3 创建实体类
- 3.4 创建 Repository 接口
- 3.5 创建 Service 层
- 4、MyBatis字段映射
如有侵权,请联系~
如有错误,也欢迎批评指正~
1、ORM架构
对象关系映射(Object Relational Mapping,简称ORM)架构是为了解决编程语言中对象和数据库中数据不一致的问题,完成对象与数据库的转换。orm除了数据库记录和程序对象的映射以外,还提供操作数据库的封装等。
优点:
开发效率:通过对象表示数据库,简化了数据库操作。
可读性:代码更接近于业务逻辑,增强了可读性。
数据库独立性:通过 ORM,可以较容易地在不同的数据库间切换。
缺点:
性能开销:在某些情况下,ORM 可能会引入性能问题,特别是在进行复杂查询时。
学习曲线:对于初学者,理解 ORM 的工作原理可能需要时间。
常见的 ORM 框架
Hibernate: Java 中最常用的 ORM 框架,功能强大,支持多种数据库。是一个强大、方便、全自动化【sql都不需要写】的持久化架构。适用于性能要求不高的系统。
MyBatis: MyBatis提供了小巧、高效,半自动化【sql仍然需要写】的持久层架构。半自动化缺点就是需要自己编写sql逻辑,当然这也是优点,可以让用户定制sql。适用于性能要求高、灵活的系统。
2、Spring整合MyBatis使用
2.1 添加maven依赖
<dependencies>
<!-- Spring Boot Starter Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- MyBatis Spring Boot Starter -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version> <!-- 使用最新版本 -->
</dependency>
<!-- MySQL Driver -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Spring Boot Starter Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
2.2 配置数据源
在 src/main/resources/application.yml 或 application.properties 中配置连接数据库的信息。以application.properties为例子:
// 数据源部分
spring.datasource.url=jdbc:mysql://localhost:3306/your_database_name
spring.datasource.username=your_username
spring.datasource.password=your_password
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
mybatis.mapper-locations=classpath:mapper/*.xml // 设置 MyBatis XML 映射文件路径
mybatis.type-aliases-package=com.example.model // 设置实体类所在的包
2.3 创建实体类
创建一个实体类与数据库表映射。例如,我们创建一个 User 类:
package com.example.model;
public class User {
private Long id;
private String name;
private Integer age;
// Getters and Setters
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
2.4 创建 MyBatis Mapper
创建一个 Mapper 接口,并使用 MyBatis 注解或 XML 文件进行 SQL 操作。
2.4.1 使用MyBatis注解
package com.example.mapper;
import com.example.model.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
@Mapper
public interface UserMapper {
@Select("SELECT * FROM user")
List<User> findAll();
}
2.4.2 使用XML方式
在 src/main/resources/mapper 目录下创建 UserMapper.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.example.mapper.UserMapper">
<select id="findAll" resultType="com.example.model.User">
SELECT * FROM user
</select>
</mapper>
创建UserMapper 接口:
package com.example.mapper;
import com.example.model.User;
import java.util.List;
public interface UserMapper {
List<User> findAll();
}
2.5 Service 层
创建一个 Service 类调用 Mapper 接口:
package com.example.service;
import com.example.mapper.UserMapper;
import com.example.model.User;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserService {
@Autowire
private final UserMapper userMapper;
public List<User> getAllUsers() {
return userMapper.findAll();
}
}
3、Spring整合Hibernate使用
3.1 添加maven依赖
<dependencies>
<!-- Spring Boot Starter Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Data JPA -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- Hibernate Validator for JPA -->
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>
<!-- MySQL Driver -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Spring Boot Starter Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
3.2 配置数据源
在 src/main/resources/application.yml 或 application.properties 文件中配置数据库连接信息。以application.properties为例:
// 数据源
spring.datasource.url=jdbc:mysql://localhost:3306/your_database_name
spring.datasource.username=your_username
spring.datasource.password=your_password
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
3.3 创建实体类
创建一个实体类,与数据库表映射。例如,我们创建一个 User 类:
package com.example.model;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private Integer age;
// Getters and Setters
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
3.4 创建 Repository 接口
创建一个 Repository 接口,以便与数据库进行交互:
package com.example.repository;
import com.example.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository<User, Long> {
// 可以添加自定义查询方法
}
3.5 创建 Service 层
创建一个 Service 类来调用 Repository 进行业务逻辑处理:
package com.example.service;
import com.example.model.User;
import com.example.repository.UserRepository;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserService {
@Autowire
private UserRepository userRepository;
public List<User> getAllUsers() {
return userRepository.findAll();
}
public void saveUser(User user) {
userRepository.save(user);
}
public User getUserById(Long id) {
return userRepository.findById(id).orElse(null);
}
public void deleteUser(Long id) {
userRepository.deleteById(id);
}
}
4、MyBatis字段映射
ORM框架主要的作用之一就是对象和数据库记录的映射。MyBatis就是从ResultSet结果集中获取到sql记录,然后将记录映射到自定义的java对象。MyBatis映射的方式:
- 使用列名直接进行映射,这也是默认的方式。这需要java的属性名和mysql字段名保持一致。
- 使用别名映射。select查询之后使用as给列起别名
- 使用ResultMap进行映射。ResultMap实现查询结果与java对象映射
- 自定义TypeHandler映射,java实现BaseTypeHandler。
主要三大步:
1、通过反射创建对象。进行字段映射的核心类DefaultResultSetHandler,创建java对象通过反射创建的:
private <T> T instantiateClass(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
try {
Constructor<T> constructor;
if (constructorArgTypes == null || constructorArgs == null) {
constructor = type.getDeclaredConstructor();
if (!constructor.isAccessible()) {
constructor.setAccessible(true);
}
return constructor.newInstance();
}
constructor = type.getDeclaredConstructor(constructorArgTypes.toArray(new Class[constructorArgTypes.size()]));
if (!constructor.isAccessible()) {
constructor.setAccessible(true);
}
// 这里创建对象
return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()]));
} catch (Exception e) {
StringBuilder argTypes = new StringBuilder();
if (constructorArgTypes != null && !constructorArgTypes.isEmpty()) {
for (Class<?> argType : constructorArgTypes) {
argTypes.append(argType.getSimpleName());
argTypes.append(",");
}
argTypes.deleteCharAt(argTypes.length() - 1); // remove trailing ,
}
StringBuilder argValues = new StringBuilder();
if (constructorArgs != null && !constructorArgs.isEmpty()) {
for (Object argValue : constructorArgs) {
argValues.append(String.valueOf(argValue));
argValues.append(",");
}
argValues.deleteCharAt(argValues.length() - 1); // remove trailing ,
}
throw new ReflectionException("Error instantiating " + type + " with invalid types (" + argTypes + ") or values (" + argValues + "). Cause: " + e, e);
}
}
2、使用resultMap将resultSet中的数据解析出数据。这个类会根据上述说的映射方式resultMap进行映射,其实上述这么多映射方式,最终都是转换为TypeHandler类【子类,如LongTypeHandler、LocalDateTypeHandler】
public class DefaultResultSetHandler implements ResultSetHandler {
@Override
public List<Object> handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
final List<Object> multipleResults = new ArrayList<Object>();
int resultSetCount = 0;
// 获取sql查询到的结果集
ResultSetWrapper rsw = getFirstResultSet(stmt);
// 所有配置的映射规则
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
validateResultMapsCount(rsw, resultMapCount);
while (rsw != null && resultMapCount > resultSetCount) {
ResultMap resultMap = resultMaps.get(resultSetCount);
// 进行映射处理,存储到multipleResults中
handleResultSet(rsw, resultMap, multipleResults, null);
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
String[] resultSets = mappedStatement.getResultSets();
if (resultSets != null) {
while (rsw != null && resultSetCount < resultSets.length) {
ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
if (parentMapping != null) {
String nestedResultMapId = parentMapping.getNestedResultMapId();
ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
handleResultSet(rsw, resultMap, null, parentMapping);
}
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
}
return collapseSingleResultList(multipleResults);
}
private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
try {
if (parentMapping != null) {
handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
} else {
if (resultHandler == null) {
DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
// 处理映射存储到defaultResultHandler中
handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
multipleResults.add(defaultResultHandler.getResultList());
} else {
handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
}
}
} finally {
// issue #228 (close resultsets)
closeResultSet(rsw.getResultSet());
}
}
public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
if (resultMap.hasNestedResultMaps()) {
ensureNoRowBounds();
checkResultHandler();
handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
} else {
handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
}
}
// 最终执行这个方法
private boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, ResultLoaderMap lazyLoader, String columnPrefix)
throws SQLException {
final List<String> mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix);
boolean foundValues = false;
final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
for (ResultMapping propertyMapping : propertyMappings) {
String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
if (propertyMapping.getNestedResultMapId() != null) {
// the user added a column attribute to a nested result map, ignore it
column = null;
}
if (propertyMapping.isCompositeResult()
|| (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH)))
|| propertyMapping.getResultSet() != null) {
// 这里会根据java对象的类型使用不同的xxxTypeHandler获取value
Object value = getPropertyMappingValue(rsw.getResultSet(), metaObject, propertyMapping, lazyLoader, columnPrefix);
// issue #541 make property optional
final String property = propertyMapping.getProperty();
if (property == null) {
continue;
} else if (value == DEFERED) {
foundValues = true;
continue;
}
if (value != null) {
foundValues = true;
}
if (value != null || (configuration.isCallSettersOnNulls() && !metaObject.getSetterType(property).isPrimitive())) {
// gcode issue #377, call setter on nulls (value is not 'found')
// 将value赋值给属性
metaObject.setValue(property, value);
}
}
}
return foundValues;
}
}
3、利用反射将对象给对象相关属性赋值。将获取到的数据利用反射给create好的对象赋值:
private void setBeanProperty(PropertyTokenizer prop, Object object, Object value) {
try {
Invoker method = metaClass.getSetInvoker(prop.getName());
Object[] params = {value};
try {
method.invoke(object, params);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
} catch (Throwable t) {
throw new ReflectionException("Could not set property '" + prop.getName() + "' of '" + object.getClass() + "' with value '" + value + "' Cause: " + t.toString(), t);
}
}