【问题标题】:Vaadin Combobox and LazyInitializationExceptionVaadin 组合框和 LazyInitializationException
【发布时间】:2022-12-22 15:40:29
【问题描述】:

我的问题可能很简单,但我不明白为什么 Vaadin 组合框会在我不调用这些集合项的情况下尝试获取在组合框中设置的嵌套实体的集合。

看到这个:

@Entity
public class Estado extends AbstractEntity {
    
    private String nome;
    private String sigla;
    @OneToMany(mappedBy = "estado")
    private List<Municipio> municipios;
    
    
    public String getNome() {
        return nome;
    }
    public void setNome(String nome) {
        this.nome = nome;
    }
    public String getSigla() {
        return sigla;
    }
    public void setSigla(String sigla) {
        this.sigla = sigla;
    }
    public List<Municipio> getMunicipios() {
        return municipios;
    }
    public void setMunicipios(List<Municipio> municipios) {
        this.municipios = municipios;
    }

private void initCbEstados() {
    
    if (cbEstados.isEmpty()) {
        List<Estado> estados = estadoService.findAllEager();
        cbEstados.setItems(estados);
    }

    cbEstados.addValueChangeListener(e -> updateCbMunicipios());

    cbEstados.setClearButtonVisible(true);

    cbEstados.setItemLabelGenerator(Estado::getNome);

    cbEstados.setWidth("50%");
    
}

private void updateViewToEdit(){
    
    if (isEditMode) {
        
        Estado estado = entity.getEndereco().getEstado();
        
    ***//this throws LazyInitializationException***
        cbEstados.setValue(estado);
        
        updateCbMunicipios();
    }

我不会随时致电 estado.get Municipios。但显然组合框的行为试图推断在 municipios 中发布了异常。

这是预期的行为吗?

我不认为它应该是?

【问题讨论】:

  • updateViewToEdit()entity 来自哪里?似乎您没有在事务中运行。假设您正在使用 Spring Data JPA 运行 Spring Boot。我关注了Thorben Janssens tips并配置了spring.jpa.open-in-view=false。我的服务方法上也有 @Transactional。所以在你的情况下就是findAllEager()
  • 您能否发布堆栈跟踪并显示 finalAllEager 在做什么?
  • 谢谢西蒙,我已经解决了自己的问题。

标签: vaadin


【解决方案1】:

抱歉,关于堆栈溢出的第一篇文章,我不熟悉该平台,最终没有发布整个代码。错误发生在

cbEstados.addValueChangeListener(e -> updateCbMunicipios());

在调试期间不可见,因为它发生在 vaadin 类中。通过在中搜索 municpios 解决了这个问题updateCbMunicpios();所以当表单库打电话进入前(进入事件前事件)读Bean()设置值cbEstados那火ValuechangeListener并加载延迟初始化异常因为 Entity 来自 Request Scoope 而不是 Fully filed 来设置 Municipio 的值cb市政厅.而 cbMunicipios 将是空的,从 Vaadin 触发一个更严重的错误。

public class FornecedorForm extends CadastroFormBaseGenerics<Fornecedor, FornecedorService> {

    private static final long serialVersionUID = -5599427454458715210L;

    EstadoService estadoService;

    @PropertyId("ativo")
    private Checkbox ckAtivo;

    @PropertyId("razaoSocial")
    private TextField txtRazaoSocial;

    @PropertyId("nomeFantasia")
    private TextField txtNomeFantasia;

    @PropertyId("cnpj")
    private TextField txtCnpj;

    @PropertyId("contato")
    private TextField txtContato;

    @PropertyId("telefoneContato")
    private TextField txtTelContato;

    @PropertyId("telefone")
    private TextField txtTelefone;

    @PropertyId("observacao")
    TextArea txtObservacao;

    private ComboBox<Estado> cbEstados;

    private ComboBox<Municipio> cbMunicipios;

    private TextField txtCep;

    private TextField txtLogradouro;

    private TextField txtNumero;

    private TextField txtComplemento;

    @PropertyId("emailContato")
    EmailField txtEmailContato;

    private FormLayout layout;

    HorizontalLayout layoutEstado;

    public FornecedorForm(@Autowired EstadoService estadoService) {
        super(Fornecedor.class, "Fornecedor");
        this.estadoService = estadoService;

        updateViewToEdit();

    }

    protected void configureAdditionalBinds() {

        getBinder().bind(cbEstados, "endereco.estado");
        getBinder().bind(cbMunicipios, "endereco.municipio");
        getBinder().bind(txtCep, "endereco.cep");
        getBinder().bind(txtLogradouro, "endereco.logradouro");
        getBinder().bind(txtNumero, "endereco.numero");
        getBinder().bind(txtComplemento, "endereco.complemento");

    }

    private void initCbEstados() {

        cbEstados = new ComboBox<Estado>("Estado");

        List<Estado> estados = estadoService.findAllEager();
        cbEstados.setItems(estados);

        cbEstados.addValueChangeListener(e -> updateCbMunicipios());

        cbEstados.setClearButtonVisible(true);

        cbEstados.setItemLabelGenerator(Estado::getNome);

        cbEstados.setWidth("50%");

    }

    private void initCbMunicpios() {

        cbMunicipios = new ComboBox<Municipio>("Município");
        cbMunicipios.setItemLabelGenerator(Municipio::getNome);
        cbMunicipios.setWidth("50%");
        cbMunicipios.setReadOnly(true);

    }

    private void updateCbMunicipios() {

        if (cbEstados.getValue() != null) {

            cbMunicipios.clear();

            cbMunicipios.setReadOnly(false);

            Estado estado = estadoService.findMunicipios(cbEstados.getValue());

            cbMunicipios.setItems(estado.getMunicipios());

        } else {
            cbMunicipios.clear();
            cbMunicipios.setReadOnly(true);
        }

    }

    @Override
    protected void initViewComponents() {

        layoutEstado = new HorizontalLayout();

        layout = new FormLayout();

        layoutEstado.setSizeFull();

        initCbEstados();
        initCbMunicpios();

        ckAtivo = new Checkbox("Ativo ?", true);
        txtRazaoSocial = new TextField("Razão Social");
        txtNomeFantasia = new TextField("Nome Fantasia");
        txtCnpj = new TextField("CNPJ");
        txtCnpj.setWidth("25%");
        // txtCnpj.setErrorMessage("CNPJ Incorreto.");
        // txtCnpj.setPattern("^[0-9]{2}?[-s.]?[0-9]{3}[-s.]?[0-9]{3}[-s/]?[0-9]{4}[-s-]?[0-9]{2}$");

        txtTelefone = new TextField("Telefone");

        txtContato = new TextField("Nome Contato");

        txtTelContato = new TextField("Telefone Contato");

        txtEmailContato = new EmailField("Email Contato");
        txtEmailContato.setClearButtonVisible(true);
        txtEmailContato.setErrorMessage("Formato do E-mail incorreto");
        txtEmailContato.setRequiredIndicatorVisible(false);

        txtLogradouro = new TextField("Logradouro");

        txtNumero = new TextField("Número");
        txtNumero.setWidth("50px");

        txtCep = new TextField("CEP");
        txtCep.setPattern("^[0-9]{5}?[-s-]?[0-9]{3}$");
        txtCep.setPlaceholder("00000-000");
        txtCep.setRequired(true);
        txtCep.setErrorMessage("Informe o CEP");

        txtCep.setWidth("75px");

        txtComplemento = new TextField("Complemento");

        txtObservacao = new TextArea("Observações");

        layout.add(ckAtivo, 2);
        layout.add(txtCnpj, 2);
        layout.add(txtRazaoSocial, txtNomeFantasia);

        layout.add(layoutEstado, 2);

        layout.add(txtLogradouro, txtNumero);
        layout.add(txtComplemento, txtCep, txtTelefone);

        HorizontalLayout layoutContato = new HorizontalLayout(txtContato, txtTelContato, txtEmailContato);
        layoutContato.setSizeFull();
        txtContato.setWidth("40%");
        txtEmailContato.setWidth("40%");
        txtTelContato.setWidth("20%");

        layout.add(layoutContato, 2);

        layout.add(txtObservacao, 2);

        super.formLayout.add(layout);

        layoutEstado.add(cbEstados, cbMunicipios);

        configureAdditionalBinds();

    }

    protected void updateViewToEdit() {

        if (isEditMode) {

            Estado estado = entity.getEndereco().getEstado();
            estado = estadoService.findMunicipios(estado);

            cbEstados.setValue(estado);

        }

    }

    @Override
    protected void getNewEntityToPersist() {

        entity = new Fornecedor();
        entity.setEndereco(new Endereco());
        ckAtivo.setValue(true);

    }

}

public abstract class CadastroFormBaseGenerics<T, SERVICE extends ServiceInterface<T>> extends FormLayout
        implements BeforeEnterObserver {

    private static final long serialVersionUID = 7069232922824142288L;

    protected T entity;
    @Autowired
    private SERVICE service;

    private final Class<T> beanClass;

    private Binder<T> binder;

    private String nomeDoBean;
    protected boolean isEditMode;

    private Button btnSave = new Button("Salvar");
    private Button btnDelete = new Button("Excluir");
    private Button btnCancel = new Button("Cancelar");

    protected VerticalLayout formLayout = new VerticalLayout();;

    private HorizontalLayout buttonsLayout = new HorizontalLayout();

    public CadastroFormBaseGenerics(Class<T> beanClass, String nomeDoBean) {

        this.beanClass = beanClass;

        this.nomeDoBean = nomeDoBean;

    }

    protected abstract void initViewComponents();

    @PostConstruct
    private void inicialize() {

        checkEditMode();

        initBinder();

        initViewComponents();

        addComponentAsFirst(formLayout);

        configureFormLayout();

        getEntityToEdit();

        updateViewToEdit();

    }

    protected abstract void updateViewToEdit();

    protected void initBinder() {

        binder = new BeanValidationBinder<T>(beanClass);

    }

    protected void setPlaceHolders(String beanName) {

        String primeiraLetra = "" + beanName.charAt(0);

        primeiraLetra = primeiraLetra.toUpperCase();
        char letraMaiuscula = primeiraLetra.charAt(0);

        char[] cArray = beanName.toCharArray();

        cArray[0] = letraMaiuscula;

        beanName = new String(cArray);

        for (int i = 0; i < formLayout.getComponentCount(); i++) {

            Component c = formLayout.getComponentAt(i);

            if (c.getClass().getName().equals("com.vaadin.flow.component.textfield.TextField")) {

                TextField aux = (TextField) c;
                aux.setPlaceholder(aux.getLabel() + " " + beanName + "...");
                aux.setClearButtonVisible(true);

            }

        }
    }

    protected void configureFormLayout() {

        formLayout.setSizeFull();

        // formLayout.setMargin(true);
        formLayout.setSpacing(true);
        formLayout.setPadding(true);
        formLayout.setAlignItems(Alignment.AUTO);
        formLayout.setMargin(true);
        // add(formLayout, 2);

        configureButtonsLayout();

    }

    void configureButtonsLayout() {

        btnDelete.setVisible(false);

        btnSave.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
        btnDelete.addThemeVariants(ButtonVariant.LUMO_ERROR);

        buttonsLayout = new HorizontalLayout();

        buttonsLayout.add(btnSave, btnDelete, btnCancel);
        buttonsLayout.setMargin(true);

        buttonsLayout.setPadding(true);
        buttonsLayout.setSizeFull();

        configureButtonEvents();

        add(buttonsLayout, 2);

    }

    private void configureButtonEvents() {

        btnSave.addClickListener(saveEvent -> save());
        btnDelete.addClickListener(deleteEvent -> delete());
        btnCancel.addClickListener(cancelEvent -> clearForm());

    }

    private void save() {

        String msg = nomeDoBean + " Alterada(o) com sucesso.";

        if (!isEditMode) {

            msg = nomeDoBean + " Cadastrada(o) com sucesso.";
            getNewEntityToPersist();

            // getNewEntity();

        }

        try {
            getBinder().writeBean(entity);
            service.save(entity);
            clearForm();
            showSucess(msg);

        } catch (DoisCsCrudException e) {

            showWarnig(nomeDoBean + " já cadastrado. Verique.");
            logException(e, "Metodo save()");

        } catch (ValidationException e) {

            showWarnig("Verique os campos obrigatórios.");

        }

    }

    /**
     * {@summary} Você deve implementar o {@code} entity = New POJO este método é
     * chamado toda vez que {@code} save() para criar um novo BEAN a ser persistido.
     */
    protected abstract void getNewEntityToPersist();

    private void delete() {
        try {

            service.delete(entity);
            clearForm();
            showSucess(nomeDoBean + " removida(o) com sucesso.");

        } catch (DoisCsCrudException e) {
            showWarnig(nomeDoBean + " não pode ser removida(o), pois existem registros que dependem dele. %/n "
                    + "Inative-o para que não seja mais exibido.");
        }

    }

    @SuppressWarnings("unchecked")
    private void getEntityToEdit() throws ClassCastException {

        if (isEditMode) {

            btnDelete.setVisible(true);
            this.entity = (T) VaadinServletRequest.getCurrent().getAttribute("entityToEdit");

        }

    }

    private boolean checkEditMode() {

        if (VaadinServletRequest.getCurrent().getAttribute("entityToEdit") != null) {

            isEditMode = true;
        } else {
            isEditMode = false;
        }

        return isEditMode;

    }

    /**
     * Sempre deve ser chamado após um click no botão salvar, cancelar ou excluir
     */
    protected void clearForm() {

        // Desabilita o botão para excluir um Bean Cadastrado
        btnDelete.setVisible(false);

        // Seta editmode para falso, garantido que a proxima interação
        // seja com um bean novo
        isEditMode = false;

        clearBinder();

    }

    protected void clearBinder() {

        binder.readBean(null);

    }

    protected void showSucess(String message) {

        createNotification(message, NotificationVariant.LUMO_SUCCESS, 3000);

    }

    protected void showError(String message) {

        createNotification(message, NotificationVariant.LUMO_ERROR, 5000);

    }

    protected void showWarnig(String message) {
        createNotification(message, NotificationVariant.LUMO_CONTRAST, 10000);

    }

    private void createNotification(String message, NotificationVariant variant, int duration) {

        Notification n = new Notification(message);
        n.setDuration(7000);
        n.addThemeVariants(variant);
        n.setPosition(Position.TOP_CENTER);

        n.open();

    }

    protected void logException(Exception e, String logDesc) {

        System.out.println("################  " + logDesc.toUpperCase() + " $$ " + getClass().getSimpleName()
                + "  ################");

        e.printStackTrace();

    }

    public Binder<T> getBinder() {
        return binder;
    }

    @Override
    public void beforeEnter(BeforeEnterEvent event) {

        binder.bindInstanceFields(this);
        if (entity != null) {

            getBinder().readBean(entity);

        }

    }

}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-09-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-06-10
    相关资源
    最近更新 更多