Weld (CDI) и Datanucleus (JPA) не играют хорошо, есть ли элегантный обходной путь? - PullRequest
2 голосов
/ 03 апреля 2012

... по крайней мере, если концепция «элегантного обходного пути» действительно имеет смысл!

Вот некоторые подробности:

При использовании CDI и JPA вы часто будете нуждаться вчтобы получить доступ к одному из ваших управляемых JPA bean-компонентов в выражении EL на одной из ваших страниц JSF, например:

<html xmlns="http://www.w3.org/1999/xhtml" 
      xmlns:h="http://java.sun.com/jsf/html" 
      xmlns:f="http://java.sun.com/jsf/core">
  <h:head>
    <title>An Everyday Facelet</title>
  </h:head>
  <h:body>
    <h1>Behold, a jpa-managed bean property!</h1>
    <h:outputText value="#{exampleEntity.name}" />
  </h:body>
</html>

Для простоты мы будем предполагать, что эта страница является результатом публикации HTTP ичто клиент ввел значение для свойства name на предыдущей странице.Сам бин может быть простой сущностью jpa, полученной из метода продюсера, например:

@Entity
public class ExampleEntity implements Serializable {
    @Id
    @Column
    private Long entityId;
    @Column
    private String name;

    public ExampleEntity() {
    }

    public void setEntityId(Long entityId) {
        this.entityId = entityId;
    }
    public Long getEntityId() {
        return entityId;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
}

// Some method in another class
public class AnotherClass {
    @Produces
    @RequestScoped
    @Named("exampleEntity")
    public ExampleEntity getExampleEntity() {
        return new ExampleEntity();
    }
}

Хорошо, давайте скомпилируем.Одним из важных аспектов реализации JPA ядра данных является то, что существует этап после компиляции.Вам нужно запустить средство улучшения байт-кода в ваших скомпилированных классах jpa, поэтому давайте предположим, что мы это сделали.При попытке загрузить свой контейнер сервлета (т. Е. Tomcat, Jetty) вы получите исключение при запуске Weld (более одного, если вы на самом деле пытаетесь внедрить этот bean-компонент куда-нибудь).Предполагая, что мои классы находятся в пакете 'testapp', исключение выглядит примерно так:

org.jboss.weld.exceptions.UnproxyableResolutionException: WELD-001437 Normal scoped bean class testapp.ExampleEntity is not proxyable because the type is final or it contains a final method public final java.lang.Object testapp.ExampleEntity.jdoGetObjectId() - Managed Bean [class testapp.ExampleEntity] with qualifiers [@Any @Default @Named].

public final java.lang.Object jdoGetObjectId ()?Я не определил и не объявил один из них, хороший веб-сервер.Вы, должно быть, ошибаетесь!

Оказывается, энхансер datanucleus должен добавлять этот последний метод в мой скомпилированный класс.Веб-сервер запускается нормально, если я его не запускаю, хотя я предполагаю, что мои данные на самом деле не сохранятся.После небольшого исследования я смог найти объяснение проблемы в документации Weld: http://docs.jboss.org/weld/reference/1.1.5.Final/en-US/html/injection.html#d0e1429

В двух словах, у именованных компонентов не может быть окончательных методов.Следуя некоторым советам, я нашел рабочее решение.Я объявляю интерфейс для моего bean-компонента jpa и создаю метод продюсера для получения конкретного класса (который на самом деле является расширенным классом datanucleus, но Weld не знает).Вот дополнительный код:

public interface ExampleEntity {
    public Long getEntityId();
    public void setEntityId(Long entityId);
    public String getName();
    public void setName(String name);
}

Теперь, когда ExampleEntity является чисто интерфейсом, я создаю управляемую JPA реализацию без аннотаций CDI

@Entity
public class ExampleEntityImpl implements ExampleEntity, Serializable {
    // Same as the ExampleEntity class above
}

И, наконец, возможно, где-то в контроллере,Я определяю метод производителя для получения ExampleEntities

@Named("exampleEntityController")
@RequestScoped
public class ExampleEntityController {

    private ExampleEntity newExampleEntity;

    public ExampleEntityController() {
        newExampleEntity = new ExampleEntityImpl();
    }

    // The producer method
    @Produces
    @RequestScoped
    @Named("exampleEntity")
    public ExampleEntity getExampleEntity() {
        return newExampleEntity;
    }

    // Other stuff, because surely a controller does more than just this, right?
}

И ... это работает.Запускается веб-сервер, без исключений.Атрибут name надлежащим образом извлекается в этом примерном фасете (при условии, что данные уже были введены на предыдущей странице).Однако создание одноразовых интерфейсов и методов-производителей для каждого бина, управляемого jpa, довольно избыточно и уродливо.Есть ли лучшее решение?Datanucleus является требованием для этого проекта, поэтому я не могу просто использовать что-то еще.Если есть более «изящный обходной путь», я полагаю, что это лучший способ получить Weld, чтобы у меня был последний метод, который я никогда не буду использовать в одном из именованных бинов.

РедактироватьСпасибо за ответы / исправления, но я ищу способ избежать создания интерфейса для каждого бина jpa.Я удалил смешивание аннотаций JPA / CDI из примеров, но веб-сервер по-прежнему выдает одно и то же исключение независимо.Я вижу, что мне в любом случае понадобится метод продюсера.

Ответы [ 2 ]

1 голос
/ 04 апреля 2012

Вы не должны использовать CDI для своих сущностей.

  • сущности создаются вами с помощью оператора new
  • Компоненты CDI создаются контейнером, чтобы онможет обрабатывать инъекции и инициализацию
1 голос
/ 03 апреля 2012

Не смешивайте аннотации CDI с JPA.Что вам действительно нужно сделать, так это создать продюсера для любой сущности, которую вы хотите использовать с CDI.JPA необходимо создать экземпляр, тогда производитель разрешит использовать его в среде CDI или присвоить имя для использования EL.

...