Mybatis (с версии 3.3.5) не имеет элегантного способа решить эту проблему.
Вы можете устранить некоторые дубликаты, используя описанную ниже технику, но
- не все
- на стоимость усложнения кода.
CrudMapper
Вы можете попытаться (несколько) избавиться от дублирования в интерфейсе mapper, определив универсальный mapper следующим образом:
interface CrudMapper<T> {
T getById(int id);
T getByName(String name);
void create(T);
void update(T);
}
А затем используйте его для определения отдельных сопоставителей для сущностей:
interface AuthorMapper extends CrudMapper<Author> {}
interface WriterMapper extends CrudMapper<Writer> {}
Дискриминатор с xml
Вы также можете попробовать использовать дискриминатор дляповторно использовать карту результатов:
<resultMap id="workerResult" type="Worker">
<id property="id" column="id" />
<result property="name" column="name"/>
<discriminator javaType="string" column="worker_type">
<case value="author" resultType="Author"/>
<case value="writer" resultType="Writer"/>
</discriminator>
</resultMap>
Но это требует усложнения запроса, а именно добавления нового столбца worker_type
в каждый запрос выбора:
<select id="getByName" parameterType="String" resultMap="workerResult">
SELECT 'actor' as worker_type, id, name FROM actors WHERE name = #{name}
</select>
К сожалению, нет способа избежатьсоздание отдельных методов в xml mapper.Единственное, что вы можете сделать, это использовать макросы скорости, чтобы запрос находился в одном месте (а именно в макрокоманде скорости).В этом случае метод может выглядеть следующим образом:
<select id="getByName" parameterType="String" resultMap="workerResult">
#select_by_name('actor')
</select>
И макрос будет:
#macro(select_by_name $worker_table)
SELECT '${worker_table}' as worker_type, id, name FROM ${worker_table}s WHERE name = @name
Дискриминатор в Java API
Java API может быть лучше в этом отношениино не без собственных недостатков.
public interface HierarchyMapper<T> {
@SelectProvider(method = "buildGetByName", type = HierarchySqlBuilder.class)
@Results(id = "workerResult", value = {
@Result(property = "id", column = "id", id = true),
@Result(property = "name", column = "name")
})
@TypeDiscriminator(cases = {
@Case(type = Actor.class, value = "actor"),
@Case(type = Writer.class, value = "writer")},
column = "worker_type")
T getByName(@Param("name") String name, @Param("table") String table);
}
@Mapper
public interface ActorMapper extends HierarchyMapper<Actor> {
}
public class HierarchySqlBuilder {
public static String buildGetByName(
@Param("name") String name, @Param("table") String table) {
return String.format(
"SELECT '%s' as worker_type, id, name from %s where name = #{name}", table, table);
}
}
К сожалению, я не знаю, как избежать передачи table
в картограф.Проблема здесь в том, что в этом случае нам нужно построить динамический запрос, а тип сущности (или таблица) является параметром.Где-то должна быть отправка.Один из способов - создать слой хранилища над мапперами, который будет выполнять такую диспетчеризацию следующим образом:
class WorkerRepository {
@Autowired ActorMapper actorMapper;
@Autowired WriterMapper writerMapper;
public Actor getActorByName(String name) {
return actorMapper.getByNae(name, 'actor');
}
public Writer getWriterByName(String name) {
return writerMapper.getByNae(name, 'writer');
}
}
Возможно, вы захотите пересмотреть проблему.Учитывая, что все классы имеют одинаковые поля, вы можете хранить все данные в одной таблице и иметь столбец дискриминатора, такой как worker_type
в этой таблице, чтобы знать фактический тип объекта.В этом случае вы полностью избежите этой проблемы, поскольку у вас есть одна таблица, но вы все равно можете получить разные классы в Java (возможно, с общим родителем).
spring-data-mybatis
Одна вещь, которую выможно попробовать это spring-data-mybatis .Это позволяет аннотировать сущность:
@Entity
class Actor extends LongId {
private String name;
// getters and setters
}
@Entity
class Writer extends LongId {
private String name;
// getters and setters
}
и затем определять классы репозитория, которые в основном являются репозиториями данных Spring:
public interface AuthorRepository extends CrudRepository<Author, Long> {
List<Author> findByName(String name);
}
public interface WriterRepository extends CrudRepository<Writer, Long> {
List<Writer> findByName(String name);
}
В этом случае вы вообще не создаете картографы и используетеCrudRepository
в клиентах, которые раньше использовали mybatis mappers.CrudRepository
дает базовые и дополнительные автоматически генерируемые методы на основе сигнатуры метода.Для получения более подробной информации см. Spring-data документация .