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?
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>
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.
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).
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.
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:
Mam nadzieję, że to pomoże!
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));
}
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.
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