Мощная карта результатов в Mybatis

Java MyBatis

предисловие

существуетMybatis, есть мощный функциональный элементresultMap. когда мы хотимJDBC ResultSetsВы можете почувствовать его необычность при преобразовании данных в формате . Как говорится в официальном заявлении:

resultMapэлементMyBatisсамый важный и мощный элемент в Позволяет получить от 90%JDBC ResultSetsОн освобождает код извлечения данных и в некоторых случаях позволяет выполнять операции, которые JDBC не поддерживает. Фактически, при написании кода отображения для сложных операторов, таких как соединения,resultMapМожет заменить тысячи строк кода для достижения той же функции.ResultMapИдея дизайна заключается в том, что нет необходимости настраивать явное сопоставление результатов для простых операторов, а нужно только описывать их отношения для более сложных операторов.

1. Картирование полей

существуетMybatis, самый простой способ сопоставить результаты — использовать псевдонимы типов.typeAliasesобрабатывать.

Если вы хотите это сделать, первым шагом будет настройка пути к пакету класса сущностей:

mybatis.type-aliases-package=com.xxx.entity

Все классы по этому пути будут зарегистрированы вTYPE_ALIASES容器. Когда мы указываем тип возвращаемого значения, мы можем напрямую использовать псевдоним.

Например, у нас естьUserсвоего рода:

@Data
public class User {
    private String id;
    private String username;
    private String password;
    private String address;
    private String email;
}

Если поля таблицы в базе данных совпадают сUserИмя атрибута класса такое же, мы можем использоватьresultTypeвозвращаться.

<select id="getUsers" resultType="User">
	SELECT
		u.id,
		u.username,
		u.password,
		u.address,
		u.email
	FROM
		USER u
</select>

Конечно, в идеале это тот случай, когда имена свойств и полей полностью совпадают. Но на самом деле есть нестыковки, на этот раз нашиresultMapсобирается дебютировать.

еслиUserКласс остался прежним, ноSQLЗаявление изменилось, т.idизменился наuid.

<select id="getUsers" resultType="User">
	SELECT
		u.id as uid,
		u.username,
		u.password,
		u.address,
		u.email
	FROM
		USER u
</select>

Тогда в результирующем наборе мы потеряемidданные. Тогда мы можем определитьresultMap, чтобы сопоставить различные поля.

<resultMap id="getUserByIdMap" type="User">
	<result property="id" column="uid"></result>
</resultMap>

Затем ставим вышеуказанноеselectв предложенииresultTypeпревратиться вresultMap="getUserByIdMap".

здесьcolumnСоответствует имени столбца или псевдониму базы данных;propertyСоответствует полям или атрибутам набора результатов.

ЭтоresultMapСамое простое и основное использование: сопоставление полей.

Ниже мы рассмотрим, как применяются другие теги.

имя элемента описывать
constructor Используется для ввода результата в конструктор при создании экземпляра класса.
association связать объект
collection Связать несколько объектов

Во-вторых, метод построения.

Если вы хотите ввести результат в конструктор, вы можете использоватьconstructorэлемент.

Например, нашUserКласс добавляет конструктор:

public User(String id, String name) {
	this.id = id+"--------";
	this.username = name+"--------";
}

нам надоresultMapопределено вconstructorэлемент:

<resultMap id="getUserByIdMap" type="User">
	<constructor>
		<idArg column="id" name="id" javaType="string"></idArg>
		<arg column="username" name="name" javaType="string"></arg>
	</constructor>
</resultMap>

в,columnПредставляет имя или псевдоним поля базы данных;nameимя параметра в конструкторе;javaTypeУказывается тип параметра.

Как вы понимаете, после указания такого конструктора наш результирующий набор имеет видid和usernameсвойства изменятся.

{
    "id": "1001--------",
    "username": "后羿--------",
    "password": "123456",
    "address": "北京市海淀区",
    "email": "510273027@qq.com"
}

3. Ассоциация

В реальном бизнесе наши пользователи обычно играют определенную роль. затем вUserКласс обычно представлен классом сущностей.

@Data
public class User {
    //省略用户属性...
	
    //角色信息
    private Role role;
}

Когда мы запрашиваем пользователя, если мы также хотим увидеть информацию о его роли, мы напишем оператор запроса следующим образом:

<select id="getUserById" resultType="User">
        SELECT
            u.id,
            u.username,
            u.password,
            u.address,
            u.email,
            r.id as 'role_id',
            r.name as 'role_name'
        FROM
            USER u
                LEFT JOIN user_roles ur ON u.id = ur.user_id
                LEFT JOIN role r ON r.id = ur.role_id
        where u.id=#{id}
    </select>

Как и выше, необходимо запросить информацию об одном пользователе и роли пользователя. Но здесь мы не можем использоватьresultType=Userвозвращаться.

после всего,UserВ классе только одинRoleобъект, неrole_id和role_nameСвойства поля.

Итак, мы собираемся использоватьassociationсвязать их.

<resultMap id="userMap" type="User">
	<id property="id" column="id"></id>
	<result property="username" column="username"></result>
	<result property="password" column="password"></result>
	<result property="address" column="address"></result>
	<result property="email" column="email"></result>
	
	<association property="role" javaType="Role">
		<id property="id" column="role_id"></id>
		<result property="name" column="role_name"></result>
	</association>
</resultMap>

Наконец, мы можем отобразить информацию о роли в одном фрагменте:

{
    "id": "1001",
    "username": "后羿",
    "password": "123456",
    "address": "北京市海淀区",
    "email": "510273027@qq.com",
    "role": {
        "id": "3",
        "name": "射手"
    }
}

Фактически, если вы определите, что связанная информация一对一, есть более простой способ заменитьassociation, мы в этой статье第五部分-自动填充关联对象Давайте посмотрим, как он это делает.

4. Коллекция

1. Вложенное отображение результатов наборов

Сверху мы видим пользователя后羿, его роль射手; но в большинстве случаев у каждого из нас не может быть только одной роли. Поэтому нам нужноUserТип атрибута роли в классе изменен наList.

@Data
public class User {
    //省略用户属性...
	
    //角色信息
    private List<Role> roles;
}

Теперь он становится пользователем, соответствующим нескольким ролям, так что это не простоassociation.

так какassociationиметь дело с有一个Ассоциация типов; и вот мы有多个ассоциация типов, поэтому вам нужно использоватьcollectionАтрибуты.

наш общийresultMapстанет следующим:

<resultMap id="userMap" type="User">
	<id property="id" column="id"></id>
	<result property="username" column="username"></result>
	<result property="password" column="password"></result>
	<result property="address" column="address"></result>
	<result property="email" column="email"></result>
	
	<collection property="roles" ofType="Role">
		<id property="id" column="role_id"></id>
		<result property="name" column="role_name"></result>
	</collection>
</resultMap>

Таким образом, даже если у вас несколько символов, они будут отображаться правильно:

{
    "id": "1003",
    "username": "貂蝉",
    "password": "123456",
    "address": "北京市东城区",
    "email": "510273027@qq.com",
    "roles": [
        {
            "id": "1",
            "name": "中单"
        },
        {
            "id": "2",
            "name": "打野"
        }
    ]
}

2. Вложенный запрос Select для коллекций

В большинстве бизнес-систем у нас будет таблица меню, такая как следующая:Menuповерхность:

id name url parent_id
1 Управление системой 0
1001 Управление пользователями /user 1
1002 управление ролями /role 1
1003 Управление подразделением /employer 1
2 Мониторинг платформы 0
2001 Мониторинг системы /system/monitor 2
2002 Мониторинг данных /data/monitor 2

Здесь мы делим меню на два уровня. Когда мы возвращаем меню на фронтенд, его тоже нужно градировать, и вывести эти 7 кусков данных по горизонтали невозможно. Итак, вот нашMenuКласс сущности выглядит следующим образом:

@Data
public class Menu {
    private String id;
    private String name;
    private String url;
    private String parent_id;
    private List<Menu> childMenu;
}

Здесь используется меню первого уровня, которое содержит список меню второго уровня.childMenuПредставлять.

SQLзаявление, если нетparent_idСвойства поля, сначала проверяем все меню первого уровня:

<select id="getMenus" resultMap="menusMap">
	SELECT
		m.id,
		m.name,
		m.url,
		m.parent_id
	FROM
		m_menu m
	where 1=1
	<choose>
		<when test="parent_id!=null">
			and m.parent_id = #{parent_id}
		</when>
		<otherwise>
			and m.parent_id = '0'
		</otherwise>
	</choose>
</select>

В этом операторе запроса без передачи каких-либо параметров мы получим данные двух меню первого уровня.

Итак, как запросить всю информацию о меню и отобразить ее по уровням, когда этот метод вызывается только один раз?

ПосмотримmenusMapОпределение:

<resultMap id="menusMap" type="Menu">
	<id property="id" column="id"></id>
	<result property="name" column="name"></result>
	<result property="url" column="url"></result>
	<result property="m_desc" column="m_desc"></result>
	<result property="parent_id" column="parent_id"></result>
	
	<collection property="childMenu" ofType="Menu" select="getMenus"  column="{parent_id=id}"></collection>
</resultMap>

Сконцентрируйсяcollectionэлемент:

property="childMenu"Соответствует списку подменю в меню;

ofType="Menu"Соответствует типу возвращаемых данных;

select="getMenus"указанныйSELECTидентификатор заявления;

column="{parent_id=id}"является выражением для параметра.

этоcollectionОбщий смысл можно понять так:

пройти черезgetMenusэтоSELECT语句чтобы получить меню первого уровняchildMenuрезультат свойства; в вышеприведенномSELECT语句, вам нужно пройтиparent_idпараметр; значением этого параметра является значение в меню первого уровняid.

Таким образом, мы можем получить всю информацию о меню, которая была оценена.

[
    {
        "id": "1",
        "name": "系统管理",
        "parent_id": "0",
        "childMenu": [
            {
                "id": "1001",
                "name": "用户管理",
                "url": "/user",
                "parent_id": "1"
            },
            {
                "id": "1002",
                "name": "角色管理",
                "url": "/role",
                "parent_id": "1"
            },
            {
                "id": "1003",
                "name": "单位管理",
                "url": "/employer",
                "parent_id": "1"
            }
        ]
    },
    {
        "id": "2",
        "name": "平台监控",
        "parent_id": "0",
        "childMenu": [
            {
                "id": "2001",
                "name": "系统监控",
                "url": "/system/monitor",
                "parent_id": "2"
            },
            {
                "id": "2002",
                "name": "数据监控",
                "url": "/data/monitor",
                "parent_id": "2"
            }
        ]
    }
]

5. Автоматически заполнять связанные объекты

мы знаем, что вMybatisПри анализе возвращаемого значения.

Первый шаг — получить тип возвращаемого значения, получитьClassобъект, затем получите конструктор, установите доступность и верните экземпляр, затем снова оберните его какMetaObjectобъект.

из базы данныхrsПосле получения результата он вызоветMetaObject.setValue(String name, Object value)для заполнения объекта.

В процессе, что интересно,.отделить этоnameАтрибуты.

еслиnameсвойство содержит.符号, найти.符号Предыдущее имя свойства, рассматривайте его как объект сущности.

Возможно, описание автора здесь недостаточно интуитивное, давайте рассмотрим пример.

в этой статье第三部分, у нас есть一个用户对应一个角色пример.

в,UserКласс определяется следующим образом:

@Data
public class User {
    //省略用户属性...
	
    //角色信息
    private Role role;
}

Здесь нам не нужно определятьresultMap, вернуться напрямуюresultType=UserВот и все. Но нужноroleИзмените псевдоним информации, суть.符号

<select id="getUserList" resultType="User">
	SELECT
		u.id,
		u.username,
		u.password,
		u.address,
		u.email,
		r.id as 'role.id',
		r.name as 'role.name'
	FROM
		USER u
			LEFT JOIN user_roles ur ON u.id = ur.user_id
			LEFT JOIN role r ON r.id = ur.role_id
</select>

Таким образом, вMybatisрешитьсяrole.idсобственность, с.符号После разделения выяснилось, чтоroleПсевдоним соответствуетRoleобъект, он будет инициализирован первымRoleобъект и присвоить значениеidАтрибуты.

Соответствующий код показан на рисунке: