Định cấu hình ngủ đông (sử dụng JPA) để lưu trữ Y / N cho kiểu Boolean thay vì 0/1


80

Tôi có thể thiết lập JPA / hibernate để duy trì Booleancác loại Y/Nkhông? Trong cơ sở dữ liệu (cột được định nghĩa là varchar2(1). Nó hiện đang lưu trữ chúng dưới dạng 0/1. Cơ sở dữ liệu là Oracle.

Câu trả lời:


8

Cách duy nhất tôi đã tìm ra cách làm điều này là có hai thuộc tính cho lớp của tôi. Một làm boolean cho API lập trình không được bao gồm trong ánh xạ. Nó tham chiếu getter và setter một biến char riêng là Y / N. Sau đó, tôi có một thuộc tính được bảo vệ khác được bao gồm trong ánh xạ ngủ đông và nó getters và setters tham chiếu trực tiếp đến biến private char.

CHỈNH SỬA: Như đã chỉ ra, có các giải pháp khác được tích hợp trực tiếp vào Hibernate. Tôi để lại câu trả lời này vì nó có thể hoạt động trong các tình huống mà bạn đang làm việc với một trường kế thừa không hoạt động tốt với các tùy chọn được tích hợp sẵn. Trên hết, không có hậu quả tiêu cực nghiêm trọng nào đối với cách tiếp cận này.


1
Tôi đã phải làm điều gì đó tương tự - tôi đã thay đổi loại thành viên từ Boolean thành String. Trong getters và setters (có và đặt Boolean), tôi đã viết mã để chuyển đổi Y / N thành giá trị Boolean tương ứng.
sengs

@bernardn Không, nó là cái tốt hơn.
alexander


146

Hibernate có kiểu "yes_no" được tích hợp sẵn để làm những gì bạn muốn. Nó ánh xạ tới cột CHAR (1) trong cơ sở dữ liệu.

Lập bản đồ cơ bản: <property name="some_flag" type="yes_no"/>

Ánh xạ chú thích (phần mở rộng ngủ đông):

@Type(type="yes_no")
public boolean getFlag();

28
Đối với những người quan tâm, cũng có một loại "true_false" sẽ lưu trữ "T" hoặc "F".
Matt Solnit

Điều này đã hoạt động, nhưng tôi không thể sử dụng nó vì đó là một chú thích cụ thể ở chế độ ngủ đông. Cảm ơn vì câu trả lời. Có thể sử dụng nó trong một dự án khác.
sengs 21/07/09

điều này dành cho hibernate 4 trở lên, nghĩa là dành cho java 1.6 trở lên. không hoạt động cho chế độ ngủ đông 3. *
Storm_buster

7
@storm_buster Hibernate 4 không tồn tại khi câu trả lời này được đưa ra. Nó hoạt động hoàn toàn tốt đẹp với Hibernate 3.x và java 1.5
ChssPly76

3
Làm thế nào để đặt 0 hoặc 1 sau đó?
JavaTechnical

84

Đây là JPA thuần túy mà không sử dụng getters / setters. Kể từ năm 2013/2014, đây là câu trả lời tốt nhất mà không cần sử dụng bất kỳ chú thích cụ thể Hibernate nào, nhưng xin lưu ý rằng giải pháp này là JPA 2.1 và không có sẵn khi câu hỏi được hỏi lần đầu tiên:

@Entity
public class Person {    

    @Convert(converter=BooleanToStringConverter.class)
    private Boolean isAlive;    
    ...
}

Và sau đó:

@Converter
public class BooleanToStringConverter implements AttributeConverter<Boolean, String> {

    @Override
    public String convertToDatabaseColumn(Boolean value) {        
        return (value != null && value) ? "Y" : "N";            
        }    

    @Override
    public Boolean convertToEntityAttribute(String value) {
        return "Y".equals(value);
        }
    }

Biên tập:

Việc triển khai ở trên coi bất kỳ điều gì khác với ký tự "Y", bao gồm null, như false. Đúng không? Một số người ở đây cho rằng điều này không chính xác, và tin rằng nulltrong cơ sở dữ liệu phải là nullJava.

Nhưng nếu bạn trả về nullbằng Java, nó sẽ cho bạn NullPointerExceptionbiết nếu trường của bạn là boolean nguyên thủy . Nói cách khác, trừ một số lĩnh vực của bạn thực sự sử dụng lớp Boolean tốt nhất để xem xét nullnhư false, và sử dụng thực hiện ở trên. Sau đó, Hibernate sẽ không tạo ra bất kỳ ngoại lệ nào bất kể nội dung của cơ sở dữ liệu.

Và nếu bạn muốn chấp nhận nullvà phát ra các ngoại lệ nếu nội dung của cơ sở dữ liệu không hoàn toàn chính xác, thì tôi đoán bạn không nên chấp nhận bất kỳ ký tự nào ngoài "Y", "N" và null. Hãy làm cho nó nhất quán và không chấp nhận bất kỳ biến thể nào như "y", "n", "0" và "1", điều này sẽ chỉ khiến cuộc sống của bạn sau này khó khăn hơn. Đây là cách triển khai nghiêm ngặt hơn:

@Override
public String convertToDatabaseColumn(Boolean value) {
    if (value == null) return null;
    else return value ? "Y" : "N";
    }

@Override
public Boolean convertToEntityAttribute(String value) {
    if (value == null) return null;
    else if (value.equals("Y")) return true;
    else if (value.equals("N")) return false;
    else throw new IllegalStateException("Invalid boolean character: " + value);
    }

Và một tùy chọn khác, nếu bạn muốn cho phép nulltrong Java nhưng không cho phép trong cơ sở dữ liệu:

@Override
public String convertToDatabaseColumn(Boolean value) {
    if (value == null) return "-";
    else return value ? "Y" : "N";
    }

@Override
public Boolean convertToEntityAttribute(String value) {
    if (value.equals("-") return null;
    else if (value.equals("Y")) return true;
    else if (value.equals("N")) return false;
    else throw new IllegalStateException("Invalid boolean character: " + value);
    }

Bộ chuyển đổi hiển thị ý tưởng, nhưng tất nhiên là không hoạt động. Bộ chuyển đổi sử dụng các giá trị có thể có của Y, NT. Tôi cũng không chắc người ta nên bỏ qua trường hợp có giá trị null do chuyển đổi.
Matthias

@Matthias Có, T là lỗi đánh máy. Tôi sửa nó rồi. Cảm ơn.
MarcG

Tôi đã có một vấn đề bằng cách sử dụng lĩnh vực với JPQL Y / N và đăng một câu hỏi followup ở đây: stackoverflow.com/questions/39581225/...
Chris Williams

11

Tôi đã sử dụng khái niệm từ câu trả lời được đăng bởi @marcg và nó hoạt động tốt với JPA 2.1. Mã của anh ấy không đúng lắm, vì vậy tôi đã đăng bản triển khai đang làm việc của mình. Điều này sẽ chuyển đổi Booleancác trường thực thể thành một cột ký tự Y / N trong cơ sở dữ liệu.

Từ lớp thực thể của tôi:

@Convert(converter=BooleanToYNStringConverter.class)
@Column(name="LOADED", length=1)
private Boolean isLoadedSuccessfully;

Lớp chuyển đổi của tôi:

/**
 * Converts a Boolean entity attribute to a single-character
 * Y/N string that will be stored in the database, and vice-versa
 * 
 * @author jtough
 */
public class BooleanToYNStringConverter 
        implements AttributeConverter<Boolean, String> {

    /**
     * This implementation will return "Y" if the parameter is Boolean.TRUE,
     * otherwise it will return "N" when the parameter is Boolean.FALSE. 
     * A null input value will yield a null return value.
     * @param b Boolean
     */
    @Override
    public String convertToDatabaseColumn(Boolean b) {
        if (b == null) {
            return null;
        }
        if (b.booleanValue()) {
            return "Y";
        }
        return "N";
    }

    /**
     * This implementation will return Boolean.TRUE if the string
     * is "Y" or "y", otherwise it will ignore the value and return
     * Boolean.FALSE (it does not actually look for "N") for any
     * other non-null string. A null input value will yield a null
     * return value.
     * @param s String
     */
    @Override
    public Boolean convertToEntityAttribute(String s) {
        if (s == null) {
            return null;
        }
        if (s.equals("Y") || s.equals("y")) {
            return Boolean.TRUE;
        }
        return Boolean.FALSE;
    }

}

Biến thể này cũng rất thú vị nếu bạn yêu thích các biểu tượng cảm xúc và cảm thấy mệt mỏi với Y / N hoặc T / F trong cơ sở dữ liệu của mình. Trong trường hợp này, cột cơ sở dữ liệu của bạn phải là hai ký tự thay vì một. Có lẽ không phải là một vấn đề lớn.

/**
 * Converts a Boolean entity attribute to a happy face or sad face
 * that will be stored in the database, and vice-versa
 * 
 * @author jtough
 */
public class BooleanToHappySadConverter 
        implements AttributeConverter<Boolean, String> {

    public static final String HAPPY = ":)";
    public static final String SAD = ":(";

    /**
     * This implementation will return ":)" if the parameter is Boolean.TRUE,
     * otherwise it will return ":(" when the parameter is Boolean.FALSE. 
     * A null input value will yield a null return value.
     * @param b Boolean
     * @return String or null
     */
    @Override
    public String convertToDatabaseColumn(Boolean b) {
        if (b == null) {
            return null;
        }
        if (b) {
            return HAPPY;
        }
        return SAD;
    }

    /**
     * This implementation will return Boolean.TRUE if the string
     * is ":)", otherwise it will ignore the value and return
     * Boolean.FALSE (it does not actually look for ":(") for any
     * other non-null string. A null input value will yield a null
     * return value.
     * @param s String
     * @return Boolean or null
     */
    @Override
    public Boolean convertToEntityAttribute(String s) {
        if (s == null) {
            return null;
        }
        if (HAPPY.equals(s)) {
            return Boolean.TRUE;
        }
        return Boolean.FALSE;
    }

}

Việc mở hộp 'b.booleanValue ()' có cần thiết không?
LeOn - Han Li

Ngoại trừ thuộc NULLtính cho NULLgiá trị cơ sở dữ liệu, câu trả lời này không cung cấp bất kỳ giá trị gia tăng nào cho câu trả lời @MarKG. Tốt nhất hãy nắm bắt sự khác biệt đó dưới dạng nhận xét.
Mohnish

Bạn 5 năm quá muộn chàng
Jim Tough

2

Để thậm chí làm tốt hơn ánh xạ boolean tới Y / N, hãy thêm vào cấu hình ngủ đông của bạn:

<!-- when using type="yes_no" for booleans, the line below allow booleans in HQL expressions: -->
<property name="hibernate.query.substitutions">true 'Y', false 'N'</property>

Bây giờ bạn có thể sử dụng boolean trong HQL, ví dụ:

"FROM " + SomeDomainClass.class.getName() + " somedomainclass " +
"WHERE somedomainclass.someboolean = false"

2
Đây là toàn cầu. Không đủ cho các thuộc tính số ít.
maxxyme

0

Để làm điều đó theo cách JPA chung bằng cách sử dụng chú thích getter, ví dụ dưới đây phù hợp với tôi với Hibernate 3.5.4 và Oracle 11g. Lưu ý rằng getter và setter ( getOpenedYnStringsetOpenedYnString) được ánh xạ là các phương thức riêng tư. Các phương thức đó cung cấp ánh xạ nhưng tất cả các truy cập có lập trình vào lớp đều sử dụng phương thức getOpenedYnsetOpenedYn.

private String openedYn;

@Transient
public Boolean getOpenedYn() {
  return toBoolean(openedYn);
}

public void setOpenedYn(Boolean openedYn) {
  setOpenedYnString(toYesNo(openedYn));
}

@Column(name = "OPENED_YN", length = 1)
private String getOpenedYnString() {
  return openedYn;
}

private void setOpenedYnString(String openedYn) {
  this.openedYn = openedYn;
}

Đây là lớp sử dụng với các phương thức tĩnh toYesNotoBoolean:

public class JpaUtil {

    private static final String NO = "N";
    private static final String YES = "Y";

    public static String toYesNo(Boolean value) {
        if (value == null)
            return null;
        else if (value)
            return YES;
        else
            return NO;
    }

    public static Boolean toBoolean(String yesNo) {
        if (yesNo == null)
            return null;
        else if (YES.equals(yesNo))
            return true;
        else if (NO.equals(yesNo))
            return false;
        else
            throw new RuntimeException("unexpected yes/no value:" + yesNo);
    }
}

-1

sử dụng bộ chuyển đổi JPA 2.1 là giải pháp tốt nhất, tuy nhiên nếu bạn đang sử dụng phiên bản JPA cũ hơn, tôi có thể đề xuất thêm một giải pháp (hoặc cách giải quyết khác). Tạo một enum có tên BooleanWrapper với 2 giá trị T và F và thêm phương thức sau vào nó để nhận giá trị được bao bọc public Boolean getValue() { return this == T; }:, ánh xạ nó với @Enumerated (EnumType.STRING).

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.