использовал раньшеMybatis-Plus
, если честно, я лично предпочитаюMybatis-Plus
.
ORM
Используются еще два фреймворка.JPA
а такжеMybatis
. Говорят, что домашнее использованиеMybatis
Более того, для иностранного использованияJPA
более.
а такжеMybatis-Plus
вMybatis
На основе , было добавлено много новых функций.
Давайте придерживаться функций, представленных на официальном сайте, и это многословно:
- Неинвазивность: делайте только улучшения без изменений, их внедрение не повлияет на существующие проекты, гладко как шелк.
- Низкие потери: базовый CURD автоматически вводится при запуске, производительность практически без потерь и прямая объектно-ориентированная операция.
- Мощные операции CRUD: встроенный общий преобразователь и общая служба, большинство операций CRUD для одной таблицы можно реализовать с небольшой настройкой и более мощными условными конструкторами для удовлетворения различных потребностей использования.
- Поддержка вызова лямбда-форм: с помощью лямбда-выражений вы можете легко написать различные условия запроса, и вам не нужно беспокоиться о написании неправильных полей.
- Поддерживает автоматическую генерацию первичного ключа: поддерживает до 4 стратегий первичного ключа (включая распределенный генератор уникальных идентификаторов — Sequence), которые можно свободно настроить для идеального решения проблемы первичного ключа.
- Поддержка режима ActiveRecord: поддержка вызова формы ActiveRecord, класс сущностей может выполнять мощные операции CRUD только путем наследования класса Model.
- Поддержка настраиваемых глобальных общих операций: поддержка внедрения глобального общего метода (запись один раз, использование в любом месте)
- Встроенный генератор кода: используйте код или подключаемый модуль Maven для быстрого создания кода уровня Mapper, Model, Service, Controller, механизма поддержки шаблонов и других настраиваемых конфигураций, ожидающих вашего использования.
- Встроенный плагин пейджинга: на основе физического пейджинга MyBatis разработчикам не нужно заботиться о конкретных операциях.После настройки плагина запись пейджинга эквивалентна обычному запросу списка.
- Плагин подкачки поддерживает несколько баз данных: MySQL, MariaDB, Oracle, DB2, H2, HSQL, SQLite, Postgre, SQLServer и т. д.
- Встроенный плагин анализа производительности: может выводить Sql-операторы и время их выполнения.Рекомендуется включать эту функцию во время разработки и тестирования, что позволяет быстро выявлять медленные запросы
- Встроенный подключаемый модуль глобального перехвата: обеспечивает интеллектуальный анализ и блокировку операций удаления и обновления для всей таблицы, а также может настраивать правила перехвата для предотвращения ошибочных операций.
Подробности смотрите на официальном сайте:mybatis.plus/Новое доменное имя официального сайта тоже корова🍺. В любом случае, все сказано и сделано.
Что касаетсяJPA
, хотя я лично думаю, что это слишком жестко, но есть и места, которые стоит изучить.
давно, сMybatis-Plus
Есть хлопотная проблема, то есть если набор данных существует в нескольких таблицах, эти таблицы могут быть один-к-одному, один-ко-многим или многие-к-одному, то мне нужно настроить несколько, если я хочу найти их всех.Mapper
метод запроса. Количество строк кода сильно увеличивается.
видел это раньшеMybatis-Plus
Исходный код , я думал как сделатьMybatis-Plus
Поддержка запросов на объединение нескольких таблиц. Но найти было трудно. потому чтоMybatis-Plus
Нижний слой поддерживает только одну таблицу.
видел недавноJPA
из@OneToOne
,@OneToMany
,@ManyToMany
Эти аннотации, вдруг у меня в голове мелькнула мысль, будто что-то вродеJPA
Насколько проще использовать аннотации таким образом?
Заранее заявляю, это все мои собственные мысли, я не виделJPA
исходный код, поэтому реализация может быть такой же, какJPA
Разные.
просто сделай это
- добавить аннотацию
- Обработка аннотаций
- Релиз пакета
Некоторые люди могут не знать, на самом делеMybatis
Он также поддерживает перехватчики, в этом случае вы можете использовать перехватчики для обработки аннотаций.
Аннотация One2One
@Inherited
@Documented
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface One2One {
/**
* 本类主键列名
*/
String self() default "id";
/**
* 本类主键在关联表中的列名
*/
String as();
/**
* 关联的 mapper
*/
Class<? extends BaseMapper> mapper();
}
Скажем, если есть две таблицы,A
а такжеB
это отношение один к одному,A
поверхностьid
существуетB
столa_id
, связанные таким образом.
существуетA
используйте эту аннотацию в классе сущностей,self
то естьid
,а такжеas
то естьa_id
,это значитA
изid
в видеa_id
поинтересоваться, иmapper
то естьB
изMapper
, ниже приведен примерA
то естьUserAccount
,B
то естьUserAddress
.
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@ApiModel(value = "UserAccount对象", description = "用户相关")
public class UserAccount extends Model<UserAccount> {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "id")
@TableId(value = "id", type = IdType.AUTO)
private Long id;
@ApiModelProperty(value = "昵称")
private String nickName;
@TableField(exist = false)
//把id的值 作为userId 在 UserAddressMapper中 查询
@One2One(self = "id", as = "user_id", mapper = UserAddressMapper.class)
private UserAddress address;
@Override
protected Serializable pkVal() {
return this.id;
}
}
Перехватчик Mybatis One2OneInterceptor
Я не буду подробно представлять здесь перехватчик, я уже писал несколько статей о перехватчике Mybatis, если вам интересно, вы можете пойти и посмотреть его.
Перехватчик Mybatis реализует хранение и запрос данных типа Geometry.
Перехватчик Mybatis печатает полный SQL
@Intercepts({
@Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})
})
@Slf4j
public class One2OneInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
Object result = invocation.proceed();
if (result == null) {
return null;
}
if (result instanceof ArrayList) {
ArrayList list = (ArrayList) result;
for (Object o : list) {
handleOne2OneAnnotation(o);
}
} else {
handleOne2OneAnnotation(result);
}
return result;
}
@SneakyThrows
private void handleOne2OneAnnotation(Object o) {
Class<?> aClass = o.getClass();
Field[] fields = aClass.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
One2One one2One = field.getAnnotation(One2One.class);
if (one2One != null) {
String self = one2One.self();
Object value = MpExtraUtil.getValue(o, self);
String as = one2One.as();
Class<? extends BaseMapper> mapper = one2One.mapper();
BaseMapper baseMapper = SpringBeanFactoryUtils.getApplicationContext().getBean(mapper);
QueryWrapper<Object> eq = Condition.create().eq(as, value);
Object one = baseMapper.selectOne(eq);
field.set(o, one);
}
}
}
@Override
public Object plugin(Object o) {
return Plugin.wrap(o, this);
}
@Override
public void setProperties(Properties properties) {
}
}
Mybatis
Перехватчики могут перехватывать для различных сценариев, таких как:
-
Executor
: метод перехвата исполнителя. -
ParameterHandler
: Перехватить обработку параметров. -
ResultHandler
: Прервать обработку результирующего набора. -
StatementHandler
: перехватывает обработку сборок синтаксиса Sql.
Здесь нужно найти аннотацию возвращаемого объекта путем перехвата набора результатов.После нахождения аннотации он автоматически запросит базу данных в соответствии с конфигурацией аннотации.После нахождения результата данные будут инкапсулированы в возвращаемый набор результатов. . Это избавит вас от необходимости настраивать его несколько раз.Mapper
метод запроса.
Сложность: Хотя в аннотации указано, чтоMapper
, а то что получается в перехватчике это ещеBaseMapper
, при использованииBaseMapper
Это действительно сложно сделать запрос, я перепробовал много методов, но все в порядке.Mybatis-Plus
поддерживать использованиеCondition.create().eq(as, value);
Условия сваркиSQL
, то вы можете использоватьbaseMapper.selectOne(eq);
чтобы узнать.
public class MpExtraUtil {
@SneakyThrows
public static Object getValue(Object o, String name) {
Class<?> aClass = o.getClass();
Field[] fields = aClass.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
if (field.getName().equals(name)) {
return field.get(o);
}
}
throw new IllegalArgumentException("未查询到名称为:" + name + " 的字段");
}
}
MpExtraUtil
использовать отражение, чтобы получитьid
значение .
Поговорим об аннотации «многие ко многим».
@Inherited
@Documented
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Many2Many {
/**
* 本类主键列名
*/
String self() default "id";
/**
* 本类主键在中间表的列名
*/
String leftMid();
/**
* 另一个多方在中间表中的列名
*/
String rightMid();
/**
* 另一个多方在本表中的列名
*/
String origin();
/**
* 关联的 mapper
*/
Class<? extends BaseMapper> midMapper();
/**
* 关联的 mapper
*/
Class<? extends BaseMapper> mapper();
}
Предположим, естьA
,A_B
,B
три стола, вA
Используйте эту аннотацию в классе сущностейself
то естьA
первичный ключ таблицыid
,leftMid
то естьA
Таблицаid
имя в промежуточной таблице, т.е.a_id
,а такжеrightMid
даB
Имя первичного ключа таблицы в промежуточной таблице:b_id
, origin
то естьB
Исходное имя собственного первичного ключа таблицы, то естьid
,midMapper
это промежуточная таблицаMapper
, это,A_B
соответствующийMapper
,mapper
даB
ТаблицаMapper
.
Это действительно немного извилисто.
есть еще один@One2Many
не сказать и@One2One
то же, что и дляMany2One
, с другой точки зрения, это@One2One
.
инструкции
Сначала создается таблица, а затем генератор кода генерирует код одним щелчком мыши.
CREATE TABLE `user_account` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
`nick_name` varchar(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '昵称',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='用户相关';
CREATE TABLE `user_address` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '地址id',
`user_id` bigint(20) DEFAULT NULL COMMENT '用户id',
`address` varchar(200) DEFAULT NULL COMMENT '详细地址',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
CREATE TABLE `user_class` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '课程id',
`class_name` varchar(20) DEFAULT NULL COMMENT '课程名称',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
CREATE TABLE `user_hobby` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '爱好id',
`user_id` bigint(20) DEFAULT NULL COMMENT '用户id',
`hobby` varchar(40) DEFAULT NULL COMMENT '爱好名字',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
CREATE TABLE `user_mid_class` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '中间表id',
`user_id` bigint(20) DEFAULT NULL COMMENT '用户id',
`class_id` bigint(20) DEFAULT NULL COMMENT '课程id',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
Добавьте зависимости, которые были опубликованы в центральном репозитории и могут использоваться напрямую:
<dependency>
<groupId>top.lww0511</groupId>
<artifactId>mp-extra</artifactId>
<version>1.0.1</version>
</dependency>
Добавить аннотацию к классу запуска
@EnableMpExtra
Настроить перехватчик
Потому что общий проект будет настраивать свои собственныеMybatisConfiguration
, после того, как я настрою его здесь, упакую, а затем импортирую, он не вступит в силу.
Поэтому я подумал о компромиссе.
доMybatisConfiguration
черезnew
из, теперь черезMybatisExtraConfig.getMPConfig();
получить, таким образом, полученныйMybatisConfiguration
Перехватчик уже добавлен.
всеMybatis-Plus
Пример класса конфигурации, обратите внимание на строку 43:
@Slf4j
@Configuration
@MapperScan(basePackages = "com.ler.demo.mapper", sqlSessionTemplateRef = "sqlSessionTemplate")
public class MybatisConfig {
private static final String BASE_PACKAGE = "com.ler.demo.";
@Bean("dataSource")
public DataSource dataSource() {
try {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost/mp-extra?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghai");
dataSource.setUsername("root");
dataSource.setPassword("adminadmin");
dataSource.setInitialSize(1);
dataSource.setMaxActive(20);
dataSource.setMinIdle(1);
dataSource.setMaxWait(60_000);
dataSource.setPoolPreparedStatements(true);
dataSource.setMaxPoolPreparedStatementPerConnectionSize(20);
dataSource.setTimeBetweenEvictionRunsMillis(60_000);
dataSource.setMinEvictableIdleTimeMillis(300_000);
dataSource.setValidationQuery("SELECT 1");
return dataSource;
} catch (Throwable throwable) {
log.error("ex caught", throwable);
throw new RuntimeException();
}
}
@Bean(name = "sqlSessionFactory")
public SqlSessionFactory sqlSessionFactory(@Qualifier("dataSource") DataSource dataSource) throws Exception {
MybatisSqlSessionFactoryBean factoryBean = new MybatisSqlSessionFactoryBean();
factoryBean.setDataSource(dataSource);
factoryBean.setVfs(SpringBootVFS.class);
factoryBean.setTypeAliasesPackage(BASE_PACKAGE + "entity");
Resource[] mapperResources = new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/*.xml");
factoryBean.setMapperLocations(mapperResources);
// 43行 获取配置
MybatisConfiguration configuration = MybatisExtraConfig.getMPConfig();
configuration.setDefaultScriptingLanguage(MybatisXMLLanguageDriver.class);
configuration.setJdbcTypeForNull(JdbcType.NULL);
configuration.setMapUnderscoreToCamelCase(true);
configuration.addInterceptor(new SqlExplainInterceptor());
configuration.setUseGeneratedKeys(true);
factoryBean.setConfiguration(configuration);
return factoryBean.getObject();
}
@Bean(name = "sqlSessionTemplate")
public SqlSessionTemplate sqlSessionTemplate(@Qualifier("sqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
@Bean(name = "transactionManager")
public PlatformTransactionManager platformTransactionManager(@Qualifier("dataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
@Bean(name = "transactionTemplate")
public TransactionTemplate transactionTemplate(@Qualifier("transactionManager") PlatformTransactionManager transactionManager) {
return new TransactionTemplate(transactionManager);
}
}
Установление отношений в классах сущностей
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@ApiModel(value = "UserAccount对象", description = "用户相关")
public class UserAccount extends Model<UserAccount> {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "id")
@TableId(value = "id", type = IdType.AUTO)
private Long id;
@ApiModelProperty(value = "昵称")
private String nickName;
@TableField(exist = false)
//把id的值 作为userId 在 UserAddressMapper中 查询
@One2One(self = "id", as = "user_id", mapper = UserAddressMapper.class)
private UserAddress address;
@TableField(exist = false)
@One2Many(self = "id", as = "user_id", mapper = UserHobbyMapper.class)
private List<UserHobby> hobbies;
@TableField(exist = false)
@Many2Many(self = "id", leftMid = "user_id", rightMid = "class_id", origin = "id"
, midMapper = UserMidClassMapper.class, mapper = UserClassMapper.class)
private List<UserClass> classes;
@Override
protected Serializable pkVal() {
return this.id;
}
}
В основном такие комментарии. да, добавь@TableField(exist = false)
, иначе будет сообщено об ошибке.
тестовый интерфейс
@Slf4j
@RestController
@RequestMapping("/user")
@Api(value = "/user", description = "用户")
public class UserAccountController {
@Resource
private UserAccountService userAccountService;
@Resource
private UserAccountMapper userAccountMapper;
@ApiOperation("查询一个")
@ApiImplicitParams({
@ApiImplicitParam(name = "", value = "", required = true),
})
@GetMapping(value = "/one", name = "查询一个")
public HttpResult one() {
//service
UserAccount account = userAccountService.getById(1L);
//mapper
// UserAccount account = userAccountMapper.selectById(1L);
//AR模式
// UserAccount account = new UserAccount();
// account.setId(1L);
// account = account.selectById();
return HttpResult.success(account);
}
}
Интерфейс очень простой, вызов встроенногоgetById
, но нашел все соответствующие данные, все из-за настроенных аннотаций.
Видно, что на самом деле отправлено несколькоSQL
. ПервыйuserAccountService.getById(1L)
, последние отправляются автоматически.
исходный кодgit ee.com/GitHub-2635…
GitHub: GitHub.com/l2d2/tickets - о...
Примерgit ee.com/GitHub-2635…
Суммировать
Я действительно не хочу публиковать слишком много кода, но на самом деле это довольно просто. Адрес исходного кода и адрес примера все опубликованы. Если вам интересно, вы можете взглянуть. Если вы считаете, что это полезно, вы можете заказать егоStar
. Каждый может внести свой вклад.
Наконец, приглашаю всех обратить внимание на мой официальный аккаунт, вместе учиться и вместе добиваться успехов. давай 🤣
поискБлог Наньчжаообрати внимание на