Подтвердить что ты не робот

Почему ORM считается хорошим, но "выберите *" считается плохой?

Обычно ORM не делает что-то вроде select *?

Если у меня есть таблица MyThing со столбцами A, B, C, D и т.д., тогда обычно будет объект MyThing со свойствами A, B, C, D.

Было бы злым, если бы этот объект был не полностью создан экземпляром select, который выглядел так: только выборка A, B, а не C, D:

выберите A, B из MyThing/* не получите C и D, потому что они нам не нужны */

но было бы также злым всегда делать это:

выберите A, B, C, D/* получите все столбцы, чтобы мы могли полностью создать объект MyThing */

Принимает ли ORM предположение, что доступ к базе данных настолько велик, теперь вам не нужно беспокоиться об этом, и поэтому вы всегда можете получить все столбцы?

Или у вас есть разные объекты MyThing, по одному для каждой комбинации столбцов, которые могут оказаться в инструкции select?

РЕДАКТИРОВАТЬ: Прежде чем ответить на вопрос, пожалуйста, прочитайте ответы Николаса Пиасекки и Билла Карвина. Наверное, я задал свой вопрос плохо, потому что многие его неправильно поняли, но Николас понял это на 100%. Как и он, меня интересуют другие ответы.


EDIT # 2: Ссылки, относящиеся к этому вопросу:

Почему нам нужны объекты сущностей?

http://blogs.tedneward.com/2006/06/26/The+Vietnam+Of+Computer+Science.aspx, особенно раздел "Проблема частичного объекта и парадокс времени загрузки"

http://groups.google.com/group/comp.object/browse_thread/thread/853fca22ded31c00/99f41d57f195f48b?

http://www.martinfowler.com/bliki/AnemicDomainModel.html

http://database-programmer.blogspot.com/2008/06/why-i-do-not-use-orm.html

4b9b3361

Ответ 1

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

Хорошим примером может служить интернет-магазин, в котором я работаю. Он имеет объект Brand, а на главной странице веб-сайта все марки, которые продаются в магазине, перечислены с левой стороны. Чтобы отобразить это меню брендов, все потребности сайта - это целое число BrandId и строка BrandName. Но объект Brand содержит всю лодку других свойств, прежде всего свойство Description, которое может содержать существенно большой объем текста о Brand. Нет двух способов, загружая всю эту дополнительную информацию о бренде, чтобы просто выплюнуть его имя в неупорядоченном списке: (1) заметно и значительно медленно, обычно из-за больших текстовых полей и (2) довольно неэффективно, когда оно приходит к использованию памяти, созданию больших строк и даже не глядя на них, прежде чем выбросить их.

Один вариант, предоставляемый многими ORM, - это ленивая загрузка свойства. Таким образом, мы могли бы вернуть объект Brand, но это время, затрачиваемое на потребление и потеряющее память поле Description, пока мы не попытаемся вызвать его аксессуар get. В этот момент прокси-объект перехватит наш вызов и сосать описание из базы данных как раз вовремя. Это иногда достаточно хорошо, но сожгли меня достаточно времени, что я лично не рекомендую:

  • Легко забыть, что свойство лениво загружено, вводя проблему SELECT N + 1, просто написав цикл foreach. Кто знает, что происходит при подключении LINQ.
  • Что делать, если вызов базы данных "точно в срок" завершился неудачей, потому что транспорт был запутан или сеть отключена? Я почти гарантирую, что любой код, который делает что-то безобидное, как string desc = brand.Description, не ожидал, что этот простой вызов вызовет DataAccessException. Теперь вы просто врезались в неприятный и неожиданный путь. (Да, я наблюдал, как мое приложение сильно сбилось из-за этого. Узнал трудный путь!)

Итак, что я закончил, это то, что в сценариях, которые требуют производительности или подвержены привязкам к базам данных, я создаю отдельный интерфейс, который веб-сайт или любая другая программа может вызывать для доступа к определенным фрагментам данных, которые имеют тщательно изучили планы своих запросов. Архитектура в конечном итоге выглядит примерно так (простите искусство ASCII):

Web Site:         Controller Classes
                     |
                     |---------------------------------+
                     |                                 |
App Server:       IDocumentService               IOrderService, IInventoryService, etc
                  (Arrays, DataSets)             (Regular OO objects, like Brand)
                     |                                 |
                     |                                 |
                     |                                 |
Data Layer:       (Raw ADO.NET returning arrays, ("Full cream" ORM like NHibernate)
                   DataSets, simple classes)

Раньше я думал, что это обманывает, подрывая объектную модель OO. Но в практическом смысле, пока вы делаете этот ярлык для отображения данных, я думаю, все в порядке. Обновления/вставки и то, что вы по-прежнему проходите через полностью гидратированную модель домена, заполненную ORM, и то, что происходит гораздо реже (в большинстве случаев), чем отображение определенных подмножеств данных. ORM, такие как NHibernate, позволят вам делать прогнозы, но к этому моменту я просто не вижу смысла ORM. В любом случае это будет хранимая процедура, запись ADO.NET займет две секунды.

Это всего лишь мои два цента. Я с нетерпением жду некоторых других ответов.

Ответ 2

Люди используют ORM для повышения производительности разработки, а не для оптимизации производительности во время выполнения. Это зависит от проекта, важно ли повысить эффективность разработки или эффективность выполнения.

На практике можно использовать ORM для максимальной производительности, а затем профилировать приложение для выявления узких мест, как только вы закончите. Замените ORM-код на пользовательские SQL-запросы только там, где вы получите наибольший баг для доллара.

SELECT * не плохо, если вам обычно нужны все столбцы в таблице. Мы не можем обобщить, что шаблон всегда хорош или всегда плох.

edit: Re: комментарий doofledorfer... Лично я всегда называю столбцы в запросе явно; Я никогда не использую шаблон в производственном коде (хотя я использую его при выполнении специальных запросов). Первоначальный вопрос касается ORM - на самом деле это не редкость, что рамки ORM вызывают a SELECT * равномерно, чтобы заполнить все поля в соответствующей объектной модели.

Выполнение запроса SELECT * может не обязательно указывать, что вам нужны все эти столбцы, и это не обязательно означает, что вы не обращаете внимания на свой код. Возможно, структура ORM генерирует SQL-запросы, чтобы убедиться, что все поля доступны в случае необходимости.

Ответ 3

Linq to Sql или любая реализация IQueryable использует синтаксис, который в конечном итоге позволяет вам контролировать выбранные данные. Определение запроса также является определением его результирующего набора.

Это аккуратно избегает проблемы select *, удаляя обязанности формы данных из ORM.

Например, чтобы выбрать все столбцы:

from c in data.Customers
select c

Чтобы выбрать подмножество:

from c in data.Customers
select new
{
  c.FirstName,
  c.LastName,
  c.Email
}

Чтобы выбрать комбинацию:

from c in data.Customers
join o in data.Orders on c.CustomerId equals o.CustomerId
select new
{
  Name = c.FirstName + " " + c.LastName,
  Email = c.Email,
  Date = o.DateSubmitted
}

Ответ 4

Я не уверен, почему вам нужен частично гидратированный объект. Учитывая класс Клиента со свойствами Name, Address, Id. Я хотел бы, чтобы все они создали полностью заполненный объект Customer.

Список, зависающий от клиентов под названием "Заказы", ​​может быть лениво загружен при доступе, хотя большинство ORM. И NHibernate в любом случае позволяет делать проекции на другие объекты. Поэтому, если вы сказали просто список клиентов, в котором вы указали ID и имя, вы можете создать объект типа CustomerListDisplay и спроектировать свой запрос HQL в этот набор объектов и получить только нужные столбцы из базы данных.

Друзья не позволяют друзьям преждевременно оптимизировать. Полностью увлажняйте свой объект, ленивая загрузка. А затем профилируйте приложение, которое ищет проблемы и оптимизирует проблемные области.

Ответ 5

Можно рассмотреть два отдельных вопроса.

Для начала довольно часто при использовании ORM для таблицы, а объект имеет совершенно разные "фигуры", это одна из причин, по которой многие инструменты ORM поддерживают довольно сложные сопоставления.

Хорошим примером является то, что таблица частично денормализована, а столбцы содержат избыточную информацию (часто это делается для улучшения производительности запросов или отчетов). Когда это происходит, для ORM более эффективно запрашивать только требуемые столбцы, чем для того, чтобы все дополнительные столбцы были возвращены и проигнорированы.

Вопрос о том, почему "Выбор *" является злом, является отдельным, и ответ падает на две половины.

При выполнении "select *" сервер базы данных не обязан возвращать столбцы в каком-либо конкретном порядке и на самом деле может разумно возвращать столбцы в другом порядке каждый раз, хотя почти нет баз данных.

Проблема заключается в том, что, когда типичный разработчик замечает, что возвращенные столбцы, похоже, находятся в последовательном порядке, предполагается, что столбцы будут всегда быть в этом порядке, а затем вы создадите код необоснованные предположения, просто ждут неудачи. Хуже того, эта неудача не может быть фатальной, но может просто включать, скажем, использование Года Рождения вместо Баланса Счета.

Другая проблема с "Select *" вращается вокруг использования таблиц - во многих крупных компаниях администратор базы данных контролирует схему и вносит изменения в соответствии с требованиями основных систем. Если ваш инструмент выполняет "select *", вы получаете только текущие столбцы - если DBA удалил лишний столбец, который вам нужен, вы не получите ошибки, и ваш код может промахнуться вперед, вызвав все виды повреждений. Явным образом запрашивая нужные вам поля, вы гарантируете, что ваша система будет ломаться, а не обрабатывать неверную информацию.

Ответ 6

Даже ORM должны избегать использования SELECT *, используя ленивую загрузку и т.д.

И да, SELECT *, как правило, плохая идея, если вы не потребляете все данные.

Итак, у вас есть разные объекты MyThing, по одному для каждой колонки? - Кори Трейгер (15 ноября в 0:37)

Нет, у меня есть только объекты только для чтения (которые содержат только важную информацию) для таких вещей, как поиск и массивные коллекции, и конвертируют их в полностью гидратированные объекты по требованию. - Cade Roux (15 ноября в 1:22)

Ответ 7

Случай, который вы описываете, является отличным примером того, как ORM не является панацеей. Базы данных предлагают гибкий, основанный на потребностях доступ к своим данным, в основном через SQL. Как разработчик, я могу легко и просто получить все данные (SELECT *) или некоторые данные (SELECT COL1, COL2) по мере необходимости. Мой механизм для этого будет легко понят любым другим разработчиком, принимающим проект.

Чтобы получить такую ​​же гибкость от ORM, вам нужно сделать намного больше работы (либо вы, либо разработчики ORM), чтобы вернуть вас к месту под капотом, где вы либо получаете все, либо столбцов из базы данных по мере необходимости (см. превосходные ответы выше, чтобы понять некоторые проблемы). И все это лишний материал - это еще больше вещей, которые могут потерпеть неудачу, делая систему ORM по своей сути менее надежной, чем прямые вызовы SQL.

Это не означает, что вы не должны использовать ORM (моя стандартная оговорка заключается в том, что все варианты дизайна имеют издержки и выгоды, и выбор того или другого просто зависит) - выбивайте себя, если он работает для вас. Я скажу, что я действительно не понимаю популярность ORM, учитывая количество лишней работы, которая, как представляется, создает для своих пользователей. Я буду придерживаться использования SELECT *, когда (дождитесь его) мне нужно получить каждый столбец из таблицы.

Ответ 8

ORM вообще не полагаются на SELECT *, но полагаются на более эффективные методы поиска столбцов, например, определенных файлов карт данных (Hibernate, варианты Hibernate и Apache iBATIS делают это). Что-то более автоматическое можно настроить, запросив схему базы данных, чтобы получить список столбцов и их типы данных для таблицы. То, как данные заполняются, зависит от конкретной ORM, которую вы используете, и там должно быть хорошо документировано.

Никогда не рекомендуется выбирать данные, которые вы вообще не используете, поскольку это может создать ненужную зависимость кода, которая может быть неприятной для последующего обслуживания. Для работы с данными, внутренними для класса, все немного сложнее.

Коротким правилом было бы всегда извлекать все данные, которые класс сохраняет по умолчанию. В большинстве случаев небольшое количество накладных расходов не будет иметь большого значения, поэтому ваша основная цель - сократить расходы на обслуживание. Позже, когда вы выполняете профилирование кода и имеете основания полагать, что он может извлечь выгоду из настройки поведения, настало время сделать это.

Если я увидел ORM, сделайте инструкции SELECT *, видимо или под его обложками, тогда я бы посмотрел в другом месте, чтобы удовлетворить потребности в интеграции с базой данных.

Ответ 9

SELECT * - неплохо. Вы спросили, кто бы это ни считал плохим? Почему? ".

Ответ 10

SELECT * - это убедительное указание, что у вас нет контроля за дизайном вашего приложения и его модулей. Одна из основных трудностей в том, чтобы убирать кого-то еще, - это когда есть вещи, которые не имеют никакой цели, но не указывают, что нужно и что нужно использовать, а что нет.

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

Мы все знаем и презираем программистов, которые не слишком беспокоятся о том, почему все работает, они просто любят пробовать вещи до тех пор, пока ожидаемые события не произойдут и не закроют его для следующего парня. SELECT * - действительно хороший способ сделать это.

Ответ 11

Если вам кажется, что нужно инкапсулировать все внутри объекта, но нужно что-то с небольшим подмножеством того, что содержится в таблице, - определите свой собственный класс. Напишите прямой sql (внутри или без ORM - большинство разрешите прямому sql обходить ограничения) и заполнить ваш объект результатами.

Однако, я бы просто использовал представление ORM таблицы в большинстве ситуаций, если только профилирование мне не помогло.

Ответ 12

Если вы используете кеширование запросов, выбор * может быть хорошим. Если вы выбираете различный ассортимент столбцов каждый раз, когда вы попадаете в таблицу, это может быть просто получение кэшированного select * для всех этих запросов.

Думаю, вы путаете цель ORM. ORM предназначен для сопоставления модели домена или аналогичной таблицы в базе данных или в каком-либо другом хранилище данных. Это не означает, что ваше приложение более эффективно вычисляется или даже ожидается.