Trim string pole w JPA

Mam tabelę db z kolumną datatype char(20). Nie mogę go zmienić na varchar.

Piszę encję JPA zmapowaną do tej tabeli. Chciałbym, aby pole string reprezentujące tę kolumnę w mojej klasie encji zawsze zawierało przyciętą wartość, a nie 20-znakową wartość wypełnioną spacjami, która istnieje w db.

Nie widzę łatwego sposobu, by to zrobić. (annotation would rock!). W tej chwili zwracam tylko przyciętą wartość z mojego gettera (), ale to czuję się jak kludge.

Wyszukiwarka google nie oferuje żadnej pomocy w tym zakresie. Jakieś pomysły?

 27
Author: Sean Patrick Floyd, 2011-04-20

7 answers

Lub możesz użyć adnotacji cyklu życia:

@Entity
public class MyEntity {

    @PostLoad
    protected void repair(){
        if(myStringProperty!=null)myStringProperty=myStringProperty.trim();
    }

    private String myStringProperty;
    public String getMyStringProperty() {
        return myStringProperty;
    }
    public void setMyStringProperty(String myStringProperty) {
        this.myStringProperty = myStringProperty;
    }

}

Jeśli występuje to na wielu encjach, możesz utworzyć niestandardową adnotację i napisać dedykowany EntityListener.

Adnotacja

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Trim {}

Słuchacz

public class TrimListener {

    private final Map<Class<?>, Set<Field>> trimProperties = 
        new HashMap<Class<?>, Set<Field>>();

    @PostLoad
    public void repairAfterLoad(final Object entity) throws Exception {
        for (final Field fieldToTrim : getTrimProperties(entity.getClass())) {
            final String propertyValue = (String) fieldToTrim.get(entity);
            if (propertyValue != null)
                fieldToTrim.set(entity, propertyValue.trim());
        }
    }

    private Set<Field> getTrimProperties(Class<?> entityClass) throws Exception {
        if (Object.class.equals(entityClass))
            return Collections.emptySet();
        Set<Field> propertiesToTrim = trimProperties.get(entityClass);
        if (propertiesToTrim == null) {
            propertiesToTrim = new HashSet<Field>();
            for (final Field field : entityClass.getDeclaredFields()) {
                if (field.getType().equals(String.class)
                    && field.getAnnotation(Trim.class) != null) {
                    field.setAccessible(true);
                    propertiesToTrim.add(field);
                }
            }
            trimProperties.put(entityClass, propertiesToTrim);
        }
        return propertiesToTrim;
    }

}

Dodaj teraz adnotację do wszystkich odpowiednich pól tekstowych za pomocą @Trim i zarejestruj słuchacza jako domyślnego słuchacza encji w swojej persistence.xml:

<persistence-unit ..>
    <!-- ... -->
    <default-entity-listeners>
      com.somepackage.TrimListener
      and.maybe.SomeOtherListener
    </default-entity-listeners>
</persistence-unit>

 

 22
Author: Sean Patrick Floyd,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-09-11 18:29:32

Akceptowana odpowiedź (przy użyciu encji JPA / adnotacji @Trim) jest niebezpieczna. wywołanie settera na pobranym obiekcie wydaje się oznaczać obiekt jako brudny. Kiedy sam próbowałem tego na poziomie jednostki głównej (używając Spring3 / hibernate), wywołało to mnóstwo obcych aktualizacji powiązanych podmiotów, które w przeciwnym razie nie zostały zmodyfikowane podczas transakcji. To był prawdziwy bałagan w produkcji, a znalezienie go do tego, że jest przyczyną, zajęło trochę czasu.

In the end I zdecydował się na prostsze podejście do ręcznego przycinania każdego z pól na żądanie (w niestandardowym maperze entity-to-domain lub w entity getter), podobne do odpowiedzi Edwina.

 16
Author: patrickd,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-07 17:22:49

Jakiego dostawcy JPA używasz?

Jeśli używasz EclipseLink char pola są domyślnie przycięte. Możesz to wyłączyć za pomocą właściwości trymstrings sesji (upewnij się, że nie ustawiłeś tego).

 2
Author: James,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-04-20 13:05:00

Używam tej metody, która sprawia, że przycinanie jest przezroczyste bez konieczności używania adnotacji w każdym polu tekstowym. W tym samym pakiecie, w którym masz swoją klasę session factory (tą, której używasz do pobierania sesji, np. org.blablabla.yourpackage.etc.SessionGetter.getSession(), Musisz utworzyć plik o nazwie package-info.java i umieścić w nim tę zawartość:

@TypeDefs({
        @TypeDef(name = "trimmedStringType",
                defaultForType = String.class,
                typeClass = StringUserType.class)
})
package org.blablabla.yourpackage.etc;

import org.hibernate.annotations.TypeDef;
import org.hibernate.annotations.TypeDefs;

Następnie tworzysz klasę StringUserType w tym samym pakiecie:

import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.type.StandardBasicTypes;
import org.hibernate.usertype.EnhancedUserType;

import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;

public class StringUserType implements EnhancedUserType, Serializable {

    private static final int[] SQL_TYPES = new int[]{Types.VARCHAR};

    @Override
    public int[] sqlTypes() {
        return SQL_TYPES;
    }

    @Override
    public Class returnedClass() {
        return String.class;
    }

    @Override
    public boolean equals(Object x, Object y) throws HibernateException {
        if (x == y) {
            return true;
        }
        if (x == null || y == null) {
            return false;
        }
        String dtx = (String) x;
        String dty = (String) y;
        return dtx.equals(dty);
    }

    @Override
    public int hashCode(Object object) throws HibernateException {
        return object.hashCode();
    }


    @Override
    public Object nullSafeGet(ResultSet resultSet, String[] names, SessionImplementor session, Object owner)
            throws HibernateException, SQLException {
        Object s = StandardBasicTypes.STRING.nullSafeGet(resultSet, names, session, owner);
        if (s == null) {
            return null;
        }
        return s.toString().trim();
    }

    @Override
    public void nullSafeSet(PreparedStatement preparedStatement, Object value, int index, SessionImplementor session)
            throws HibernateException, SQLException {
        if (value == null) {
            StandardBasicTypes.STRING.nullSafeSet(preparedStatement, null, index, session);
        } else {
            StandardBasicTypes.STRING.nullSafeSet(preparedStatement, value.toString().trim(), index, session);
        }
    }

    @Override
    public Object deepCopy(Object value) throws HibernateException {
        return value;
    }

    @Override
    public boolean isMutable() {
        return false;
    }

    @Override
    public Serializable disassemble(Object value) throws HibernateException {
        return (Serializable) value;
    }

    @Override
    public Object assemble(Serializable cached, Object value) throws HibernateException {
        return cached;
    }

    @Override
    public Object replace(Object original, Object target, Object owner) throws HibernateException {
        return original;
    }

    @Override
    public String objectToSQLString(Object object) {
        throw new UnsupportedOperationException();
    }

    @Override
    public String toXMLString(Object object) {
        return object.toString();
    }

    @Override
    public Object fromXMLString(String string) {
        return string;
    }

}

I to wszystko, nie trzeba tworzyć niestandardowych adnotacji w fasoli, to "magicznie" przyciąć ciągi ilekroć otrzymujesz obiekty z bazy danych.

 2
Author: Mateus Viccari,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-15 19:54:00

Umieścić adnotację w metodzie getter, ustawić @Acesss na AccessType.Właściwość i trim pole tam za pomocą String.metoda trim ().

Lub po prostu umieść trim w metodzie getter i zawsze uzyskaj dostęp do pola za jej pośrednictwem. Nic prostszego nie będzie.

Jeśli nie masz nic przeciwko używaniu czystego Hibernate i odbieganiu od standardu JPA, możesz użyć Hibernate @ColumnTransformer pod warunkiem, że masz funkcję bazy danych do wykonywania pracy

Znajdziesz Jak aby to zrobić w Hibernate reference:

Http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html/mapping.html#mapping-column-read-and-write

Mam nadzieję, że to pomoże!

 1
Author: Edwin Dalorzo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-04-20 12:08:04

Wszystko, co musisz zrobić, to umieścić to na kontrolerze i działa zgodnie z oczekiwaniami nie potrzebujesz słuchacza ani niczego z tego

@InitBinder
public void initBinder(WebDataBinder binder) {
    binder.registerCustomEditor(String.class, new StringTrimmerEditor(true));
}
 1
Author: Rudy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-08 15:00:24

Jeśli Wymaganie domeny stwierdza, że potrzebuje przyciętych informacji, musisz zapisać dane w przyciętej wartości. Nie widzę w tym nic złego.

Model Domeny
Model obiektowy domena, która obejmuje zarówno zachowanie i data. (PEAA-Martin Fowler)

Jeśli wyraźnie musisz wymusić regułę biznesową na poziomie bazy danych, jedną z opcji jest możliwość napisania wyzwalacza, możesz użyć wbudowanej metody przycinania SQL. Ale będzie jak rakieta do rozbicia jajka.

 0
Author: zawhtut,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-04-20 07:35:17