Sluimerfout: een ander object met dezelfde ID-waarde was al aan de sessie gekoppeld

Ik heb in wezen enkele objecten in deze configuratie (het echte datamodel is iets complexer):

  • A heeft een veel-op-veel-relatie met B. (B heeft inverse="true")
  • B heeft een veel-op-een relatie met C. (ik heb cascadeingesteld op "save-update")
  • C is een soort type/categorietabel.

Ook moet ik waarschijnlijk vermelden dat de primaire sleutels worden gegenereerd door de database bij het opslaan.

Met mijn gegevens kom ik soms problemen tegen waarbij A een set van verschillende B-objecten heeft, en deze B-objecten verwijzen naar hetzelfde C-object.

Als ik session.saveOrUpdate(myAObject)aanroep, krijg ik een hibernate-foutmelding: "a different object with the same identifier value was already associated with the session: C". Ik weet dat de slaapstand niet hetzelfde object twee keer in dezelfde sessie kan invoegen/bijwerken/verwijderen, maar is er een manier om dit te omzeilen? Dit lijkt niet zo’n ongewone situatie te zijn.

Tijdens mijn onderzoek naar dit probleem heb ik mensen het gebruik van session.merge()zien voorstellen, maar als ik dat doe, worden alle “conflicterende” objecten als lege objecten in de database ingevoegd met alle waarden ingesteld op null. Dat is duidelijk niet wat we willen.

[Bewerken] Iets anders dat ik vergat te vermelden, is dat (om architectonische redenen die buiten mijn macht liggen), elke lees- of schrijfsessie in een aparte sessie moet worden gedaan.


Antwoord 1, autoriteit 100%

Hoogstwaarschijnlijk omdat de B-objecten niet verwijzen naar dezelfde Java C-objectinstantie. Ze verwijzen naar dezelfde rij in de database (d.w.z. dezelfde primaire sleutel), maar het zijn verschillende kopieën ervan.

Wat er dus gebeurt, is dat de Hibernate-sessie, die de entiteiten beheert, bijhoudt welk Java-object overeenkomt met de rij met dezelfde primaire sleutel.

Eén optie zou zijn om ervoor te zorgen dat de entiteiten van objecten B die naar dezelfde rij verwijzen, daadwerkelijk verwijzen naar dezelfde objectinstantie van C. U kunt ook trapsgewijze uitschakelen voor die lidvariabele. Op deze manier, wanneer B wordt volgehouden, is C dat niet. U zult C echter handmatig apart moeten opslaan. Als C een type/categorietabel is, dan is het waarschijnlijk logisch om zo te zijn.


Antwoord 2, autoriteit 31%

Stel cascade gewoon in op MERGE, dat zou voldoende moeten zijn.


Antwoord 3, autoriteit 15%

Je hoeft maar één ding te doen. Voer session_object.clear()uit en sla het nieuwe object op. Dit zal de sessie wissen (met de toepasselijke naam) en het beledigende dubbele object uit je sessie verwijderen.


Antwoord 4, autoriteit 9%

Ik ben het eens met @Hemant Kumar, heel erg bedankt. Volgens zijn oplossing heb ik mijn probleem opgelost.

Bijvoorbeeld:

@Test
public void testSavePerson() {
    try (Session session = sessionFactory.openSession()) {
        Transaction tx = session.beginTransaction();
        Person person1 = new Person();
        Person person2 = new Person();
        person1.setName("222");
        person2.setName("111");
        session.save(person1);
        session.save(person2);
        tx.commit();
    }
}

Persoon.java

public class Person {
    private int id;
    private String name;
    @Id
    @Column(name = "id")
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    @Basic
    @Column(name = "name")
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

Deze code maakt altijd fouten in mijn aanvraag:
A different object with the same identifier value was already associated with the session, later kwam ik erachter dat ik het was vergeten
om mijn primaire sleutel automatisch te vergroten!

Mijn oplossing is om deze code toe te voegen aan uw primaire sleutel:

@GeneratedValue(strategy = GenerationType.AUTO)

Antwoord 5, autoriteit 8%

Dit betekent dat u meerdere rijen in uw tabel probeert op te slaan met de verwijzing naar hetzelfde object.

controleer de id-eigenschap van uw entiteitsklasse.

@Id
private Integer id;

naar

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(unique = true, nullable = false)
private Integer id;

Antwoord 6, autoriteit 5%

Verplaats de taak van het toewijzen van de object-ID van Hibernate naar de database met behulp van:

<generator class="native"/>

Dit loste het probleem voor mij op.


Antwoord 7, autoriteit 3%

Voeg de annotatie toe
@GeneratedValue
aan de boon die u plaatst.


Antwoord 8, autoriteit 3%

Een manier om het bovenstaande probleem op te lossen is door de hashcode()te overschrijven.
Spoel ook de slaapstand voor en na het opslaan.

getHibernateTemplate().flush();

Het expliciet instellen van het ontkoppelde object op nullhelpt ook.


Antwoord 9, autoriteit 2%

Ik kwam dit bericht net tegen, maar in c#-code. Ik weet niet zeker of het relevant is (exact dezelfde foutmelding echter).

Ik was de code aan het debuggen met breekpunten en breidde een aantal collecties uit via privéleden terwijl debugger zich op een breekpunt bevond. Nadat de code opnieuw was uitgevoerd zonder door structuren te graven, verdween de foutmelding. Het lijkt erop dat NHibernate door het bekijken van privé-lazy-loaded collecties dingen heeft geladen die op dat moment niet mochten worden geladen (omdat ze in privé-leden waren).

De code zelf is verpakt in een vrij gecompliceerde transactie die een groot aantal records en veel afhankelijkheden kan bijwerken als onderdeel van die transactie (importproces).

Hopelijk een aanwijzing voor iemand anders die het probleem tegenkomt.


Antwoord 10, autoriteit 2%

Zoek het kenmerk ‘Cascade’ in de slaapstand en verwijder het. Wanneer u “Cascade” beschikbaar stelt, roept het andere bewerkingen op (opslaan, bijwerken en verwijderen) op andere entiteiten die een relatie hebben met gerelateerde klassen. Dus dezelfde identiteitswaarde zal gebeuren.
Het werkte bij mij.


Antwoord 11

Ik had deze fout een paar dagen geleden en ik heb te veel tijd besteed aan het oplossen van deze fout.

public boolean save(OrderHeader header) {
    Session session = sessionFactory.openSession();
    Transaction transaction = session.beginTransaction();
    try {
        session.save(header);
        for (OrderDetail detail : header.getDetails()) {
            session.save(detail);
        }
        transaction.commit();
        session.close();
        return true;
    } catch (HibernateException exception) {
        exception.printStackTrace();
        transaction.rollback();
        return false;
    }
}

Voordat ik deze foutmelding kreeg, had ik het ID-generatietype niet genoemd in het OrderDetil Object. wanneer het zonder het genereren van de id van Orderdetails de Id als 0 houdt voor elk OrderDetail-object. dit is wat #jbx heeft uitgelegd. Ja, dat is het beste antwoord. dit ene voorbeeld hoe het gebeurt.


Antwoord 12

Probeer de code van uw zoekopdracht eerder te plaatsen.
Dat lost mijn probleem op.
bijv. verander dit:

query1 
query2 - get the error 
update

naar dit:

query2
query1
update

Antwoord 13

het is mogelijk dat u de identifier van het object niet instelt voordat u de update-query aanroept.


Antwoord 14

Ik heb het probleem ondervonden omdat het genereren van de primaire sleutel verkeerd is, wanneer ik een rij als volgt invoeg:

public void addTerminal(String typeOfDevice,Map<Byte,Integer> map) {
        // TODO Auto-generated method stub
        try {
            Set<Byte> keySet = map.keySet();
            for (Byte byte1 : keySet) {
                Device device=new Device();
                device.setNumDevice(DeviceCount.map.get(byte1));
                device.setTimestamp(System.currentTimeMillis());
                device.setTypeDevice(byte1);
                this.getHibernateTemplate().save(device);
            }
            System.out.println("hah");
        }catch (Exception e) {
            // TODO: handle exception
            logger.warn("wrong");
            logger.warn(e.getStackTrace()+e.getMessage());
        }
}

Ik verander de klasse van de id-generator in identiteit

<id name="id" type="int">
    <column name="id" />
    <generator class="identity"  />
 </id>

Antwoord 15

In mijn geval werkte alleen flush() niet. Ik moest een clear() gebruiken na flush().

public Object merge(final Object detachedInstance)
    {
        this.getHibernateTemplate().flush();
        this.getHibernateTemplate().clear();
        try
        {
            this.getHibernateTemplate().evict(detachedInstance);
        }
}

Antwoord 16

als je EntityRepository gebruikt, gebruik dan saveAndFlush in plaats van save


Antwoord 17

Als ik een tabblad met expressies in mijn IDE open liet die een slaapstand maakte, werd het object aangeroepen dat deze uitzondering veroorzaakte. Ik probeerde hetzelfde object te verwijderen. Ook had ik een breekpunt op de verwijderaanroep die nodig lijkt te zijn om deze fout te laten gebeuren. Door eenvoudig een ander tabblad voor expressies als tabblad vooraan te maken of de instelling te wijzigen zodat de ide niet stopt bij onderbrekingspunten, is dit probleem opgelost.


Antwoord 18

Zorg ervoor dat uw entiteit hetzelfde generatietype heeft met alle toegewezen entiteiten

Bijvoorbeeld: gebruikersrol

public class UserRole extends AbstractDomain {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String longName;
private String shortName;
@Enumerated(EnumType.STRING)
private CommonStatus status;
private String roleCode;
private Long level;
@Column(columnDefinition = "integer default 0")
private Integer subRoleCount;
private String modification;
@ManyToOne(fetch = FetchType.LAZY)
private TypeOfUsers licenseType;

}

Module:

public class Modules implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String longName;
private String shortName;

}

Hoofdentiteit met toewijzing

public class RoleModules implements Serializable{
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.MERGE)
private UserRole role;
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.MERGE)
private Modules modules;
@Type(type = "yes_no")
private boolean isPrimaryModule;
public boolean getIsPrimaryModule() {
    return isPrimaryModule;
}

}


Antwoord 19

In aanvulling op alle eerdere antwoorden, een mogelijke oplossing voor dit probleem in een grootschalig project, als u een waardeobject voor uw klassen gebruikt, stelt u het id-attribuut niet in de VO Transformer-klasse in.


Antwoord 20

Een ander geval waarin hetzelfde foutbericht kan worden gegenereerd, aangepaste allocationSize:

@Id
@Column(name = "idpar")
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "paramsSequence")
@SequenceGenerator(name = "paramsSequence", sequenceName = "par_idpar_seq", allocationSize = 20)
private Long id;

zonder overeenkomst

alter sequence par_idpar_seq increment 20;

kan validatie van beperkingen veroorzaken tijdens het invoegen (dat is gemakkelijk te begrijpen) of “een ander object met dezelfde ID-waarde was al aan de sessie gekoppeld” – dit geval was minder duidelijk.


Antwoord 21

De reden voor dit probleem is dat je verschillende kopieën hebt van objecten die verwijzen naar dezelfde raw in je onderliggende tabel, dus probeer je object als een nieuw object te behandelen, maar tijdens het opslaan identificeert het dat er een raw is met dezelfde primaire sleutel. Dus het geeft bovenstaande fout.

De beste oplossing voor dit probleem is om het hele object (bovenliggende entiteit met onderliggende entiteiten) uit de database te laden (u kent de primaire sleutel van het bovenliggende object al) en vervolgens de waarden in het object dat vanuit de database is geladen, bij te werken vanuit uw nieuwe object (dat u probeerde op te slaan) en sla vervolgens het object op dat u heeft geladen uit de DB die nieuwe waarden heeft.

Hiermee worden uw waarden in de DB bijgewerkt zonder bovenstaande foutmelding te geven.

PS- U hoeft de id’s niet bij te werken omdat ze al bestaan in het object dat vanuit de database is geladen, update alleen de waarden die moeten worden gewijzigd


Antwoord 22

voer gewoon de huidige transactie uit.

currentSession.getTransaction().commit();

nu kunt u een nieuwe transactie starten en alles doen op entiteit

Other episodes