2012-04-02 6 views
2

... по крайней мере, если концепция «элегантного обходного пути» на самом деле имеет заслуги!Weld (CDI) и Datanucleus (JPA) не играют хорошо, есть ли утонченное обходное решение?

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

При использовании CDI и JPA, вы часто хотите получить доступ к одному из ваших JPA управляемой фасоли в Expression 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 datanucleus является то, что существует шаг после компиляции. Вам нужно запустить усилитель байт-кода на ваших скомпилированных jpa-классах, поэтому давайте предположим, что мы это сделали. При попытке загрузить ваш контейнер сервлета (т. Е. Tomcat, Jetty) вы получите исключение при запуске Weld (более одного, если вы действительно пытаетесь вставить этот компонент где-нибудь). Предполагая, что мои классы в пакете '' 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]. 

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

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

В двух словах, похоже, что именованные бобы не могут иметь окончательные методы. Следуя некоторым советам, я нашел рабочее решение. Я объявляю интерфейс для моего jpa bean и создаю метод производителя для получения конкретного класса (который на самом деле является расширенным классом 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 правильно извлекается в этом примере facelet (если данные уже были введены на предыдущей странице). Однако создание одноразовых интерфейсов и методов продюсеров для каждого обработанного jpa bean-компонента довольно избыточно и уродливо. Есть ли лучшее решение? Datanucleus - это требование для этого проекта, поэтому я не могу просто использовать что-то еще. Если есть более «изящное обходное решение», я предполагаю, что это предполагает лучший способ заставить Weld дать мне окончательный метод, который я никогда не буду использовать в одном из моих названных компонентов.

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

+0

К сожалению, как вы пришли к '@Named '' @ RequestScoped' @ Entity'? – Osw

+0

Ну, в этом примере область видимости произвольна, но это просто именованный компонент, который я также могу использовать с помощью JPA API. Во всяком случае, это цель. Он работает для меня на полном сервере приложений. – Shaun

+1

Усовершенствование байт-кода DataNucleus добавляет множество методов, к стандартизованному API, согласно http://db.apache.org/jdo/enhancement.html – DataNucleus

ответ

1

Вы не должны использовать CDI для своих объектов.

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

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

Смежные вопросы