Spring framework не может сохранить в базе данных с помощью EntityManager.merge()

Я пытаюсь настроить веб-сайт с помощью Spring. Мне уже удалось прочитать данные из созданной мной базы данных, но теперь я столкнулся с проблемой при попытке обновить в ней строки. Я использую функцию merge() файла EntityManager. Я не получаю никаких ошибок или исключений, он просто не обновляет данные в БД.

Я постараюсь дать весь соответствующий код.

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

Бенуцер.java

@Entity
@Table(name = "BENUTZER")
    @NamedQueries({
    @NamedQuery(name = "Benutzer.findAll", query = "SELECT tt FROM Benutzer tt"),
    @NamedQuery(name = "Benutzer.findByKennung", query = "SELECT tt FROM Benutzer tt WHERE tt.kennung = :kennung"),
public class Benutzer {

@Id
@GeneratedValue
private BigInteger oid;

@Size(min = 2, max = 20)
@Pattern(regexp = "[A-Za-z ]*", message = "Titel darf nur Buchstaben und Leerzeichen beinhalten")
private String titel;

@NotNull
@Size(min = 2, max = 25)
@Pattern(regexp = "[A-Za-z ]*", message = "Anrede darf nur Buchstaben und Leerzeichen beinhalten")
private String anrede;

@NotNull
@Size(min = 2, max = 45)
@Pattern(regexp = "[A-Za-z ]*", message = "Name darf nur Buchstaben und Leerzeichen beinhalten")
private String name;

@NotNull
@Size(min = 2, max = 45)
@Pattern(regexp = "[A-Za-z ]*", message = "Vorname darf nur Buchstaben und Leerzeichen beinhalten")
private String vorname;

@NotNull
@Size(min = 6, max = 16)
@Pattern(regexp = "[0-9]*", message = "Telefonnummer darf nur aus Zahlen bestehen")
private String telefonnummer;

@Size(max = 25, message = "Fax darf maximal 25 Zahlen beinhalten")
@Pattern(regexp = "[0-9]*", message = "Fax darf nur aus Zahlen bestehen")
private String fax;

@NotNull
@Size(min = 2, max = 45)
@Pattern(regexp = "[A-Za-z0-9 ]*", message = "Straße darf nur Buchstaben, Leerzeichen und Hausnummer beinhalten")
private String strasse;

@NotNull
@Size(min = 2, max = 45)
@Pattern(regexp = "[A-Za-z0-9 ]*", message = "Ort darf nur Buchstaben, Leerzeichen und PLZ beinhalten")
private String ort;

@NotNull
@Size(min = 2, max = 45, message = "E-mail muss zwischen 2 und 45 Zeichen beinhalten")
private String email;

private boolean freigeschaltet;

@Temporal(TemporalType.TIMESTAMP)
private Date sperrbeginn;

@NotNull(message = "Kennung darf nicht leer sein")
private String kennung;

@NotNull
private String passwort;

@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "BENUTZER_has_KUNDE", joinColumns = { @JoinColumn(name = "BENUTZER_oid") }, inverseJoinColumns = { @JoinColumn(name = "KUNDE_oid") })
private List<Kunde> kundenListe;

public Benutzer() {

};

+getter and setter
}

}

DAO: я использую в нем метод update(Benutzer benutzer), чтобы попытаться обновить соответствующий столбец в БД.

BenutzerDaoImpl.java

@Transactional
@Repository("benutzerDao")
public class BenutzerDaoImpl implements BenutzerDao{

private EntityManager em;

public EntityManager getEm() {
    return em;
}

@PersistenceContext (type = PersistenceContextType.TRANSACTION)
public void setEm(EntityManager em) {
    this.em = em;
}

public Benutzer findById(BigInteger id) {
    return em.find(Benutzer.class, id);
}

public Benutzer findByKennung(String kennung) {
    Query benutzerByKennung = em.createNamedQuery("Benutzer.findByKennung");
    benutzerByKennung.setParameter("kennung", kennung);
    return (Benutzer) benutzerByKennung.getSingleResult();
}

public List<Benutzer> findAll() {
    CriteriaBuilder cb = em.getCriteriaBuilder();
    CriteriaQuery<Benutzer> criteria = cb.createQuery(Benutzer.class);
    Root<Benutzer> benutzer = criteria.from(Benutzer.class);

    criteria.select(benutzer).orderBy(cb.asc(benutzer.get("name")));
    return em.createQuery(criteria).getResultList();
}

public void register(Benutzer benutzer) {
    em.persist(benutzer);
    return;
}

public void delete(BigInteger id) {
    Benutzer benutzerTemp = findById(id);
    em.remove(benutzerTemp);
}


public void update(Benutzer benutzer) {
    Benutzer benutzerTemp = findById(benutzer.getOid());
    benutzerTemp.setTelefon_vorwahl(benutzer.getTelefon_vorwahl());
    benutzerTemp.setTelefon_nummer(benutzer.getTelefon_nummer());
    benutzerTemp.setFax_vorwahl(benutzer.getFax_vorwahl());
    benutzerTemp.setFax_nummer(benutzer.getFax_nummer());
    benutzerTemp.setStrasse(benutzer.getStrasse());
    benutzerTemp.setStrasse_nummer(benutzer.getStrasse_nummer());
    benutzerTemp.setOrt(benutzer.getOrt());
    benutzerTemp.setEmail(benutzer.getEmail());
    benutzerTemp.setKennung(benutzer.getKennung());
    em.merge(benutzerTemp);
    return;
}

}

И класс Controller, который вызывает функцию update(Benutzer benutzer) в saveBenutzerdaten() прямо внизу кода:

BenutzerdatenController.java

@Controller
@SessionAttributes("activeUser")
public class LoginController {

private BenutzerDao benutzerDao;

private KundeDao kundeDao;

private AnlageDao anlageDao;

@Autowired 
public LoginController(BenutzerDao benutzerDao, KundeDao kundeDao, AnlageDao anlageDao){
    this.benutzerDao = benutzerDao;
    this.kundeDao = kundeDao;
    this.anlageDao = anlageDao;
}

@ModelAttribute("activeUser")
   public Benutzer populateActiveUser() {
       return new Benutzer(); // Füllt activeUser beim ersten mall wenn es null ist.
   }

@RequestMapping(value = "/login" ,method = RequestMethod.GET)
public String login(Model model, SessionStatus status) {
    status.setComplete();
    model.addAttribute("benutzerLoginDaten", new Login());
    return "login";
}


@RequestMapping(value = "/login" ,method = RequestMethod.POST)
public String requestLogin(
        @Valid @ModelAttribute("benutzerLoginDaten") Login logindaten,
        BindingResult result, Model model, final RedirectAttributes redirectAttributes) {

    if (!result.hasErrors()) {
        if (logindaten == null) {
            String error = "Fehler beim login";
            model.addAttribute("error", error);
            return "login";
        }
        Benutzer user = null;
        try{
            user = benutzerDao.findByKennung(logindaten.getKennung());
        }catch(Exception e){
            String error = "Unbekannter Benutzer";
            model.addAttribute("error", error);
            return "login";
        }
        if (user == null) {
            String error = "Unbekannter Benutzer";
            model.addAttribute("error", error);
            return "login";
        }
        String pw = user.getPasswort();
        if (pw.equals(logindaten.getPasswort())) {
            redirectAttributes.addFlashAttribute("activeUser", user);
            return "redirect:/home.html";
        } else {
            String error = "Passwort falsch";
            model.addAttribute("error", error);
            return "login";
        }
    }
    return "login";
}

@RequestMapping(value = "/home",method = RequestMethod.GET)
public String home(@ModelAttribute("activeUser") Benutzer activeUser ,Model model, final RedirectAttributes redirectAttributes){
    if(activeUser == null){
        return "redirect:/login.html";
    }
    if(activeUser.getKennung()==null){
        //falls activeUser == null, hat sich kein Benutzer eingelogt
        //und wird damit auf die loginseite weitergeleitet.
        return "redirect:/login.html";
    }

    //erstellen der Anlagenliste mit dazugehörigem Kunden:
    //i und j werden zum mitzählen der schleifendurchläufe verwenden
    //sum zählt die benotigte größe für die Anlagenliste. (summe der anlagen aller kunden zu denen der benutzer zugriff hat)
    int i=0, j = 0, sum = 0;
    List<Kunde> kunden = activeUser.getKundenListe();
    for(i = 0 ; i< kunden.size(); i++){
        List<Anlage> anlagen = kundeDao.getAllAnlagen(kunden.get(i));
        sum += anlagen.size();
    }

    //auffüllen der attribute für select anlagenListe (beschreibung: anlagenbeschreibung, ids: anlagenids):
    String[] beschreibung = new String[sum];
    BigInteger[] ids = new BigInteger[sum];

    Select anlagenListe = new Select();
    sum = 0;
    for(i = 0 ; i < kunden.size(); i++){
        List<Anlage> anlagen = kundeDao.getAllAnlagen(kunden.get(i));
        for(j = 0; j <anlagen.size(); j++){
            //beschreibung format : Anlagenbezeichnung (kunde)
            beschreibung[sum] = 
                    ""+anlagen.get(j).getBezeichnung() + 
                    " (" + 
                            ((kunden.get(i).isFirma())?
                                    kunden.get(i).getFirmenname()
                                    :
                                        kunden.get(i).getPrivatperson_nachname() +
                                    " " +
                                    kunden.get(i).getPrivatperson_vorname()) +
                     ")";
             ids[sum++]= anlagen.get(j).getOid();
         }


     }
    anlagenListe.setSelectOptions(beschreibung);
    anlagenListe.setIds(ids);
    model.addAttribute("activeUser", activeUser);
    model.addAttribute("selection", anlagenListe);
    return "home";
}


@RequestMapping(value = "/home", method = RequestMethod.POST)
public String selectAnlage(@Valid @ModelAttribute("selection") Select selection, BindingResult result, Model model) {

    if(!result.hasErrors()) {
        Anlage anlage = anlageDao.findById(selection.getSelectionOid());
        System.out.println("anlage--  id: " + anlage.getOid() + "  Bezeichnung: " + anlage.getBezeichnung());
        return "home";
    }
    return "home";

}



@RequestMapping(value = "/benutzerdaten", method = RequestMethod.GET)
public String benutzerdaten(@ModelAttribute("activeUser") Benutzer benutzer,Model model){
    return "benutzerdaten";
}


@RequestMapping(value = "/benutzerdaten", method = RequestMethod.POST)
public String saveBenutzerdaten(
        @ModelAttribute("activeUser") Benutzer benutzer,
        Model model) {
    benutzerDao.update(benutzer);
    return "benutzerdaten";
}

}

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

Кроме того, если вы заметите что-то, что я должен изменить, пожалуйста, сообщите мне. Я новичок в Spring и не уверен, что все делаю правильно. Особенно с тем, как я обращаюсь с вошедшим в систему пользователем (вероятно, это не очень хорошее решение для его сохранения в объекте DAO?).


Я нашел решение. Он отлично работал после того, как я добавил класс обслуживания и добавил его в свой mvc-dispatcher-servlet.xml. (sopro.mvc.swm.service — это пакет, в котором у меня есть классы обслуживания).


person Frager007    schedule 13.06.2014    source источник


Ответы (3)


Попробуйте следующее:

В контроллере постарайтесь не использовать private Benutzer currentlyActive как мгновенную переменную. По крайней мере, в методе saveBenutzer не используйте объект currentActive, вместо этого извлекайте идентификатор объекта из атрибута модели @ModelAttribute("newBenutzerdaten") Benutzer benutzerdaten. Сохраните идентификатор в какой-либо скрытой переменной на веб-странице, чтобы он был доступен в атрибуте модели, и передайте этот атрибут модели непосредственно в метод обновления.

person Pranav Maniar    schedule 13.06.2014
comment
Можно ли получить данные от одного контроллера, обрабатывающего, например, login.jsp, другому контроллеру, обрабатывающему home.jsp? Где я должен добавить атрибут модели? Этот вопрос является причиной, по которой я добавил текущий активный атрибут в DAO в первую очередь. - person Frager007; 14.06.2014
comment
Вы можете использовать атрибуты flash Атрибуты Spring MVC Flash.... Вот хороший учебник по использованию этих атрибутов flash во время перенаправления.. Учебное пособие по FlashAttribute - person Pranav Maniar; 14.06.2014
comment
Я избавился от мгновенной переменной, активной в данный момент в контроллере, как в классе DAO. Все равно не работает. Я сохранил пользователя в @SessionAttribute в контроллере. может ли это как-то помешать entetymanager обновить переменную Benutzer в базе данных? Поможет ли это, если я сохраню идентификатор только в атрибуте сеанса? - person Frager007; 15.06.2014
comment
@SessionAttribute не должно быть причиной проблемы. Это должно быть что-то другое... А пока не могли бы вы быстро попробовать одну вещь.. Удалите аннотацию @Transactional из dao и поместите ее в методы контроллера, который создает/обновляет/удаляет объект, и посмотрите, поможет ли это.. Я попытаюсь смоделировать проблему со своей стороны и попытаться найти ответ - person Pranav Maniar; 15.06.2014
comment
Я обновил контроллер и Дао в своем сообщении с вопросом. Было бы неплохо, если бы вы могли взглянуть на контроллер и функцию обновления от Dao. Мне потребовалось довольно много времени, чтобы отредактировать его так, как он есть сейчас. к сожалению, это все еще не работает: / Может быть, аннотация @Transactional не работает? Может быть, я неправильно настроил конфигурации весны? - person Frager007; 15.06.2014

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

Одна из часто встречающихся проблем заключается в том, что @Transactional может не работать в контексте Spring MVC, в то время как, конечно, @Controller не работает вне его. Пожалуйста, обратитесь к моему ответу в https://stackoverflow.com/a/19388280/2621917, который может быть или не быть соответствующий.

person Michael Piefel    schedule 15.06.2014
comment
Спасибо, что нашли время, чтобы отредактировать мою орфографию :) Я уверен, что мог бы разместить меньше кода, но, как я уже сказал, я новичок в Spring, и я не знаю, что имеет значение. Я просто следовал основному руководству, а после этого начал свой проект, который намного больше и сложнее, чем туто. Однако только что я добавил класс службы, и после его использования я получаю это исключение: org.springframework.transaction.TransactionSystemException: не удалось зафиксировать транзакцию JPA; вложенным исключением является javax.persistence.RollbackException: ошибка при совершении транзакции - person Frager007; 15.06.2014

Я нашел решение. Он отлично работал после того, как я добавил класс обслуживания и добавил его в свой mvc-dispatcher-servlet.xml, а также в applicationContext.xml. (sopro.mvc.swm.service — это пакет, в котором у меня есть классы обслуживания).

Спасибо ребята :)

person Frager007    schedule 15.06.2014