سؤال

لدي حقل ، يقول ، user_name, ، يجب أن تكون فريدة من نوعها في الجدول.

ما هي أفضل طريقة للتحقق منها باستخدام التحقق من صحة الربيع/السبات؟

هل كانت مفيدة؟

المحلول

أحد الحلول الممكنة هو إنشاء العرف @UniqueKey القيد (والمقابل المقابل) ؛ وللبحث عن السجلات الموجودة في قاعدة البيانات ، قدم مثيلًا EntityManager (أو السبات SessionUniqueKeyValidator.

entityManagerAwarevalidator

public interface EntityManagerAwareValidator {  
     void setEntityManager(EntityManager entityManager); 
} 

ConstrentvalIdatorFactoryImpl

public class ConstraintValidatorFactoryImpl implements ConstraintValidatorFactory {

    private EntityManagerFactory entityManagerFactory;

    public ConstraintValidatorFactoryImpl(EntityManagerFactory entityManagerFactory) {
        this.entityManagerFactory = entityManagerFactory;
    }

    @Override
    public <T extends ConstraintValidator<?, ?>> T getInstance(Class<T> key) {
        T instance = null;

        try {
            instance = key.newInstance();
        } catch (Exception e) { 
            // could not instantiate class
            e.printStackTrace();
        }

        if(EntityManagerAwareValidator.class.isAssignableFrom(key)) {
            EntityManagerAwareValidator validator = (EntityManagerAwareValidator) instance;
            validator.setEntityManager(entityManagerFactory.createEntityManager());
        }

        return instance;
    }
}

مفتاح فريد

@Constraint(validatedBy={UniqueKeyValidator.class})
@Target({ElementType.TYPE})
@Retention(RUNTIME)
public @interface UniqueKey {

    String[] columnNames();

    String message() default "{UniqueKey.message}";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    @Target({ ElementType.TYPE })
    @Retention(RUNTIME)
    @Documented
    @interface List {
        UniqueKey[] value();
    }
}

فريد من نوعه

public class UniqueKeyValidator implements ConstraintValidator<UniqueKey, Serializable>, EntityManagerAwareValidator {

    private EntityManager entityManager;

    @Override
    public void setEntityManager(EntityManager entityManager) {
        this.entityManager = entityManager;
    }

    private String[] columnNames;

    @Override
    public void initialize(UniqueKey constraintAnnotation) {
        this.columnNames = constraintAnnotation.columnNames();

    }

    @Override
    public boolean isValid(Serializable target, ConstraintValidatorContext context) {
        Class<?> entityClass = target.getClass();

        CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();

        CriteriaQuery<Object> criteriaQuery = criteriaBuilder.createQuery();

        Root<?> root = criteriaQuery.from(entityClass);

        List<Predicate> predicates = new ArrayList<Predicate> (columnNames.length);

        try {
            for(int i=0; i<columnNames.length; i++) {
                String propertyName = columnNames[i];
                PropertyDescriptor desc = new PropertyDescriptor(propertyName, entityClass);
                Method readMethod = desc.getReadMethod();
                Object propertyValue = readMethod.invoke(target);
                Predicate predicate = criteriaBuilder.equal(root.get(propertyName), propertyValue);
                predicates.add(predicate);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        criteriaQuery.where(predicates.toArray(new Predicate[predicates.size()]));

        TypedQuery<Object> typedQuery = entityManager.createQuery(criteriaQuery);

        List<Object> resultSet = typedQuery.getResultList(); 

        return resultSet.size() == 0;
    }

}

الاستخدام

@UniqueKey(columnNames={"userName"})
// @UniqueKey(columnNames={"userName", "emailId"}) // composite unique key
//@UniqueKey.List(value = {@UniqueKey(columnNames = { "userName" }), @UniqueKey(columnNames = { "emailId" })}) // more than one unique keys
public class User implements Serializable {

    private String userName;
    private String password;
    private String emailId;

    protected User() {
        super();
    }

    public User(String userName) {
        this.userName = userName;
    }
        ....
}

اختبار

public void uniqueKey() {
    EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("default");

    ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
    ValidatorContext validatorContext = validatorFactory.usingContext();
    validatorContext.constraintValidatorFactory(new ConstraintValidatorFactoryImpl(entityManagerFactory));
    Validator validator = validatorContext.getValidator();

    EntityManager em = entityManagerFactory.createEntityManager();

    User se = new User("abc", poizon);

       Set<ConstraintViolation<User>> violations = validator.validate(se);
    System.out.println("Size:- " + violations.size());

    em.getTransaction().begin();
    em.persist(se);
    em.getTransaction().commit();

        User se1 = new User("abc");

    violations = validator.validate(se1);

    System.out.println("Size:- " + violations.size());
}

نصائح أخرى

أعتقد أنه ليس من الحكمة استخدام مصادقة السبات (JSR 303) لهذا الغرض. أو من الأفضل أن يكون هدف المدقق السباتي.

JSR 303 هو حول التحقق من صحة الفول. هذا يعني التحقق مما إذا تم تعيين حقل بشكل صحيح. لكن ما تريده هو في نطاق أوسع بكثير من الفول واحد. إنه بطريقة أو بأخرى في نطاق عالمي (فيما يتعلق بجميع الفاصوليا من هذا النوع). - أعتقد أنك يجب أن تدع قاعدة البيانات تتعامل مع هذه المشكلة. اضبط قيدًا فريدًا على العمود في قاعدة البيانات الخاصة بك (على سبيل المثال عن طريق التعليق على الحقل مع @Column(unique=true)) وستتأكد قاعدة البيانات من أن الحقل فريد من نوعه.

على أي حال ، إذا كنت ترغب حقًا في استخدام JSR303 لهذا الغرض ، فمن تحتاج إلى إنشاء شرح خاص بك والمقحة الخاصة. يتعين على المدقق الوصول إلى قاعدة البيانات والتحقق مما إذا لم يكن هناك كيان آخر ذو القيمة المحددة. - لكنني أعتقد أنه سيكون هناك بعض المشكلات للوصول إلى قاعدة البيانات من المدقق في الجلسة الصحيحة.

أحد الاحتمالات هو التعليق على هذا المجال naturalid

يمكنك استخدام @Column السمة التي يمكن تعيينها على أنها unique.

يعتمد هذا الرمز على الرمز السابق الذي تم تنفيذه باستخدام EntityManager. في حال احتاج أي شخص إلى استخدام السبات Session. التعليق التوضيحي المخصص باستخدام السبات Session.
@فريدة من نوعها java

import java.lang.annotation.*;
import javax.validation.Constraint;
import javax.validation.Payload;

@Target({ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = UniqueKeyValidator.class)
@Documented
public @interface UniqueKey {
    String columnName();
    Class<?> className();
    String message() default "{UniqueKey.message}";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

unqiekeyvalidator.java

import ch.qos.logback.classic.gaffer.PropertyUtil;
import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
@Transactional
@Repository
public class UniqueKeyValidator implements ConstraintValidator<UniqueKey, String> {

    @Autowired
    private SessionFactory sessionFactory;

    public Session getSession() {
        return sessionFactory.getCurrentSession();
    }

    private String columnName;
    private Class<?> entityClass;

    @Override
    public void initialize(UniqueKey constraintAnnotation) {
        this.columnNames = constraintAnnotation.columnNames();
        this.entityClass = constraintAnnotation.className();
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        Class<?> entityClass = this.entityClass;
        System.out.println("class: " + entityClass.toString());
        Criteria criteria = getSession().createCriteria(entityClass);
        try {
                criteria.add(Restrictions.eq(this.columnName, value));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return criteria.list().size()==0;
    }
}

الاستخدام

@UniqueKey(columnNames="userName", className = UserEntity.class)
// @UniqueKey(columnNames="userName") // unique key

لقد وجدت نوعا من الحل الصعبة.

أولاً ، لقد قمت بتنفيذ المنافسة الفريدة لقاعدة بيانات MySQL الخاصة بي:

CREATE TABLE XMLTAG
(
    ID INTEGER NOT NULL AUTO_INCREMENT,
    LABEL VARCHAR(64) NOT NULL,
    XPATH VARCHAR(128),
    PRIMARY KEY (ID),
    UNIQUE UQ_XMLTAG_LABEL(LABEL)
) ;

ترى أنني أدير علامات XML التي يتم تعريفها بواسطة تسمية فريدة وحقل نص باسم "XPath".

على أي حال ، فإن الخطوة الثانية هي ببساطة التقاط الخطأ الذي أثير عندما يحاول المستخدم إجراء تحديث سيء. التحديث السيئ هو عند محاولة استبدال الملصق الحالي بتسمية موجودة. إذا تركت الملصق دون مساس ، فلا مشكلة. لذلك ، في وحدة التحكم الخاصة بي:

    @RequestMapping(value = "/updatetag", method = RequestMethod.POST)
    public String updateTag(
            @ModelAttribute("tag") Tag tag, 
            @Valid Tag validTag,
            BindingResult result,
            ModelMap map) {

        if(result.hasErrors()) {        // you don't care : validation of other
            return "editTag";       // constraints like @NotEmpty
        }
        else {
            try {
                tagService.updateTag(tag);    // try to update
                return "redirect:/tags";   // <- if it works        
            }
            catch (DataIntegrityViolationException ex) { // if it doesn't work
                result.rejectValue("label", "Unique.tag.label"); // pass an error message to the view 
                return "editTag"; // same treatment as other validation errors
            }
        }
    }

قد يتعارض هذا مع نمط unique ولكن يمكنك استخدام هذه الطريقة القذرة لتحسين إضافة الإضافة أيضًا.

ملاحظة: لا تزال هناك مشكلة واحدة: إذا تم اللحاق بأخطاء التحقق من الصحة الأخرى قبل الاستثناء ، فلن يتم عرض رسالة UNICITY.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top