จะสร้าง Metamodel เอนทิตี JPA ได้อย่างไร


97

ในจิตวิญญาณของประเภทความปลอดภัยที่เกี่ยวข้องกับCriteriaQuery JPA 2.0 ยังมี API เพื่อรองรับการแสดงMetamodelของเอนทิตี

มีใครทราบถึงการใช้งาน API นี้อย่างสมบูรณ์ (เพื่อสร้าง Metamodel ซึ่งต่างจากการสร้างคลาส metamodel ด้วยตนเอง) จะดีมากถ้ามีคนรู้ขั้นตอนในการตั้งค่านี้ใน Eclipse (ฉันคิดว่ามันง่ายเหมือนการตั้งค่าตัวประมวลผลคำอธิบายประกอบ แต่คุณไม่มีทางรู้)

แก้ไข: เพียงแค่สะดุดHibernate JPA 2 Metamodel ปั่นไฟ แต่ปัญหายังคงอยู่เนื่องจากฉันไม่พบลิงค์ดาวน์โหลดสำหรับ jar

แก้ไข 2: ในขณะที่ฉันถามคำถามนี้ แต่ฉันคิดว่าฉันจะกลับมาและเพิ่มลิงก์ไปยังโครงการHibernate JPA Model Generator บน SourceForge

คำตอบ:


88

จะดีมากถ้ามีคนรู้ขั้นตอนในการตั้งค่านี้ใน Eclipse (ฉันคิดว่ามันง่ายเหมือนการตั้งค่าตัวประมวลผลคำอธิบายประกอบ แต่คุณไม่เคยรู้)

ใช่แล้ว. นี่คือการใช้งานและคำแนะนำสำหรับการใช้งาน JPA 2.0 ต่างๆ:

EclipseLink

ไฮเบอร์เนต

OpenJPA

DataNucleus


การใช้งาน Hibernate ล่าสุดมีให้ที่:

การใช้งาน Hibernate รุ่นเก่าอยู่ที่:


1
ลิงก์ DataNucleus ตายแล้ว
Karl Richter

1
ลิงก์ไฮเบอร์เนตก็ตายเช่นกัน
Freelancer

43

โปรดดูตัวอย่าง jpa-metamodels-with-maven JPA-metamodels

ไฮเบอร์เนต

  • พวกเราต้องการ org.hibernate.org:hibernate-jpamodelgenเราจำเป็นต้อง
  • คลาสโปรเซสเซอร์คือorg.hibernate.jpamodelgen.JPAMetaModelEntityProcessor.

จำศีลเป็นการพึ่งพา

    <dependency>
      <groupId>org.hibernate.orm</groupId>
      <artifactId>hibernate-jpamodelgen</artifactId>
      <version>${version.hibernate-jpamodelgen}</version>
      <scope>provided</scope>
    </dependency>

ไฮเบอร์เนตเป็นโปรเซสเซอร์

      <plugin>
        <groupId>org.bsc.maven</groupId>
        <artifactId>maven-processor-plugin</artifactId>
        <executions>
          <execution>
            <goals>
              <goal>process</goal>
            </goals>
            <phase>generate-sources</phase>
            <configuration>
              <compilerArguments>-AaddGeneratedAnnotation=false</compilerArguments> <!-- suppress java.annotation -->
              <processors>
                <processor>org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor</processor>
              </processors>
            </configuration>
          </execution>
        </executions>
        <dependencies>
          <dependency>
            <groupId>org.hibernate.orm</groupId>
            <artifactId>hibernate-jpamodelgen</artifactId>
            <version>${version.hibernate-jpamodelgen}</version>
          </dependency>
        </dependencies>
      </plugin>

OpenJPA

  • พวกเราต้องการ org.apache.openjpa:openjpaเราจำเป็นต้อง
  • คลาสโปรเซสเซอร์คือ org.apache.openjpa.persistence.meta.AnnotationProcessor6.
  • OpenJPA <openjpa.metamodel>true<openjpa.metamodel>ดูเหมือนว่าจำเป็นต้องมีการเพิ่มเติมองค์ประกอบ

OpenJPA เป็นการพึ่งพา

  <dependencies>
    <dependency>
      <groupId>org.apache.openjpa</groupId>
      <artifactId>openjpa</artifactId>
      <scope>provided</scope>
    </dependency>
  </dependencies>
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
          <compilerArgs>
            <arg>-Aopenjpa.metamodel=true</arg>
          </compilerArgs>
        </configuration>
      </plugin>
    </plugins>
  </build>

OpenJPA เป็นโปรเซสเซอร์

      <plugin>
        <groupId>org.bsc.maven</groupId>
        <artifactId>maven-processor-plugin</artifactId>
        <executions>
          <execution>
            <id>process</id>
            <goals>
              <goal>process</goal>
            </goals>
            <phase>generate-sources</phase>
            <configuration>
              <processors>
                <processor>org.apache.openjpa.persistence.meta.AnnotationProcessor6</processor>
              </processors>
              <optionMap>
                <openjpa.metamodel>true</openjpa.metamodel>
              </optionMap>
            </configuration>
          </execution>
        </executions>
        <dependencies>
          <dependency>
            <groupId>org.apache.openjpa</groupId>
            <artifactId>openjpa</artifactId>
            <version>${version.openjpa}</version>
          </dependency>
        </dependencies>
      </plugin>

EclipseLink

  • พวกเราต้องการ org.eclipse.persistence:org.eclipse.persistence.jpa.modelgen.processorเราจำเป็นต้อง
  • คลาสโปรเซสเซอร์คือ org.eclipse.persistence.internal.jpa.modelgen.CanonicalModelProcessor.
  • EclipseLink ต้องการpersistence.xml.

EclipseLink เป็นการอ้างอิง

  <dependencies>
    <dependency>
      <groupId>org.eclipse.persistence</groupId>
      <artifactId>org.eclipse.persistence.jpa.modelgen.processor</artifactId>
      <scope>provided</scope>
    </dependency>

EclipseLink เป็นโปรเซสเซอร์

    <plugins>
      <plugin>
        <groupId>org.bsc.maven</groupId>
        <artifactId>maven-processor-plugin</artifactId>
        <executions>
          <execution>
            <goals>
              <goal>process</goal>
            </goals>
            <phase>generate-sources</phase>
            <configuration>
              <processors>
                <processor>org.eclipse.persistence.internal.jpa.modelgen.CanonicalModelProcessor</processor>
              </processors>
              <compilerArguments>-Aeclipselink.persistencexml=src/main/resources-${environment.id}/META-INF/persistence.xml</compilerArguments>
            </configuration>
          </execution>
        </executions>
        <dependencies>
          <dependency>
            <groupId>org.eclipse.persistence</groupId>
            <artifactId>org.eclipse.persistence.jpa.modelgen.processor</artifactId>
            <version>${version.eclipselink}</version>
          </dependency>
        </dependencies>
      </plugin>

DataNucleus

  • org.datanucleus:datanucleus-jpa-queryเราจำเป็นต้อง
  • คลาสโปรเซสเซอร์คือorg.datanucleus.jpa.query.JPACriteriaProcessor.

DataNucleus เป็นแหล่งอ้างอิง

  <dependencies>
    <dependency>
      <groupId>org.datanucleus</groupId>
      <artifactId>datanucleus-jpa-query</artifactId>
      <scope>provided</scope>
    </dependency>
  </dependencies>

DataNucleus เป็นโปรเซสเซอร์

      <plugin>
        <groupId>org.bsc.maven</groupId>
        <artifactId>maven-processor-plugin</artifactId>
        <executions>
          <execution>
            <id>process</id>
            <goals>
              <goal>process</goal>
            </goals>
            <phase>generate-sources</phase>
            <configuration>
              <processors>
                <processor>org.datanucleus.jpa.query.JPACriteriaProcessor</processor>
              </processors>
            </configuration>
          </execution>
        </executions>
        <dependencies>
          <dependency>
            <groupId>org.datanucleus</groupId>
            <artifactId>datanucleus-jpa-query</artifactId>
            <version>${version.datanucleus}</version>
          </dependency>
        </dependencies>
      </plugin>

3
เพื่อความชัดเจนสิ่งที่สร้างขึ้นสามารถใช้กับ eclipselink ได้แม้ว่าคุณจะใช้ hibernate เพื่อสร้างมันฉันก็ไม่สามารถสร้าง meta model จาก netbeans 8 ได้และต้องสร้างโครงการทดสอบ maven เพื่อสร้างสิ่งของของฉัน
Kalpesh Soni

@ymajoros เป็นสิ่งต้องห้ามใน SO ที่จะพูดsomething is recommendedโดยไม่ต้องIMHO? ฉันไม่ได้เป็นตัวแทนในนามของใครอื่น
Jin Kwon

1
BTW ดูคำตอบของตัวเรียงลำดับสำหรับ EclipseLink นี่คือการกำหนดค่าที่ฉันใช้มาหลายปีและทำงานได้อย่างสมบูรณ์ stackoverflow.com/questions/3037593/…
ymajoros

การใช้งานนี้ไม่เฉพาะเจาะจงฉันพยายามใช้ Hibernate metamodel ที่สร้างขึ้นกับ EclipseLink และรับ NullPointerException
Michał Ziobro

@ymajoros ยังต้องการ a persistence.xmlใช่ไหม?
Jin Kwon

20

การสนับสนุน JPA 2.0 ของ Eclipse ผ่าน Dali (ซึ่งรวมอยู่ใน "Eclipse IDE for JEE Developers") มีตัวสร้าง metamodel ของตัวเองที่รวมเข้ากับ Eclipse

  1. เลือกโครงการของคุณในPackage Explorer
  2. ไปที่Properties -> JPAกล่องโต้ตอบ
  3. เลือกโฟลเดอร์ต้นทางจากCanonical metamodel (JPA 2.0)กลุ่ม
  4. คลิกปุ่มApplyเพื่อสร้างคลาส metamodel ในโฟลเดอร์ต้นทางที่เลือก

ป้อนคำอธิบายภาพที่นี่

สิ่งนี้ควรใช้ได้กับผู้ให้บริการ JPA เนื่องจากคลาสที่สร้างขึ้นเป็นมาตรฐาน

ดูที่นี่ด้วย


มีวิธีใดบ้างที่จะเริ่มต้นกระบวนการด้วยตัวเอง? นี้ไม่น่าเชื่อถือผลิต metamodel สำหรับฉัน
thatsIch

6

สำหรับ eclipselink การอ้างอิงต่อไปนี้เท่านั้นที่เพียงพอที่จะสร้าง metamodel ไม่มีสิ่งอื่นใดที่จำเป็น

    <dependency>
        <groupId>org.eclipse.persistence</groupId>
        <artifactId>org.eclipse.persistence.jpa.modelgen.processor</artifactId>
        <version>2.5.1</version>
        <scope>provided</scope>
    </dependency>


@Barthelomeus บันทึกของคุณเป็นเท็จ EclipseLink 2.5.1+ จะสร้างคลาส metamodel สำหรับเอนทิตีที่ไม่อยู่ในรายการเช่นกันเพียงระบุ<exclude-unlisted-classes>false</exclude-unlisted-classes>ใน persisetence.xml
Michele Mariotti

โปรดทราบว่า EclipseLink จะไม่สร้างโดยไม่ต้องpersistence.xml
จินเทควันโด

6

สำหรับ Hibernate ในฐานะผู้ให้บริการซึ่งเป็น IMHO ทั่วไป:

ในกรณีของเครื่องมือสร้างเช่น Gradle Maven คุณต้องมี Hibernate JPA 2 Metamodel Generator jar ใน classpath และ compiler level> = 1.6 นั่นคือทั้งหมดที่คุณต้องสร้างโครงการและระบบจะสร้าง metamodel โดยอัตโนมัติ

ในกรณีของ IDE Eclipse 1. ไปที่ Project-> Properties-> Java Compiler-> Annotation Processing และเปิดใช้งาน 2. ขยายการประมวลผลคำอธิบายประกอบ -> เส้นทางโรงงาน -> เพิ่มโถภายนอกเพิ่มโถ Hibernate JPA 2 Metamodel Generator ตรวจสอบโถที่เพิ่มใหม่และพูดว่าตกลง ทำความสะอาดและสร้างเสร็จแล้ว!

ลิ้ง Hibernate JPA 2 Metamodel Generator jar link จาก maven repo https://mvnrepository.com/artifact/org.hibernate/hibernate-jpamodelgen


ในกรณีของฉันเพิ่ม<dependencies> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-jpamodelgen</artifactId> <scope>compile</scope> </dependency> </dependencies>ใน pom.xml ก็เพียงพอแล้ว
Lu55

ฉันต้องการการกำหนดค่าทั้งสองแบบเมื่อใช้ maven และ Eclipse หรือไม่?
Melkor

แม้ว่า hibernate-jpamodelgen จะถูกเพิ่มเข้าไปใน pom แต่ฉันก็ต้องทำสิ่งนี้และมันก็ใช้ได้
Freelancer

นี่ควรเป็นคำตอบที่ยอมรับได้เช่นกัน ขอบคุณมาก Sandeep
Shubham Arya

3

เนื่องจากนี่เป็นคำถามที่พบบ่อยมากฉันจึงเขียนบทความนี้ซึ่งคำตอบนี้อ้างอิงจาก

สมมติว่าโปรแกรมของเราจะใช้ต่อไปPost, PostComment, PostDetailsและTagหน่วยงานซึ่งรูปแบบหนึ่งต่อหลายคนหนึ่งต่อหนึ่งและอีกหลายต่อหลายความสัมพันธ์ของตาราง :

JPA Criteria Metamodel

วิธีสร้าง Metamodel เกณฑ์ JPA

hibernate-jpamodelgenเครื่องมือให้โดย Hibernate ออมสามารถนำมาใช้ในการสแกนหน่วยงานโครงการและสร้างเกณฑ์ JPA Metamodel ทั้งหมดที่คุณต้องทำคือการเพิ่มต่อไปนี้annotationProcessorPathไปmaven-compiler-pluginใน Maven pom.xmlแฟ้มการกำหนดค่า:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>${maven-compiler-plugin.version}</version>
    <configuration>
        <annotationProcessorPaths>
            <annotationProcessorPath>
                <groupId>org.hibernate</groupId>
                <artifactId>hibernate-jpamodelgen</artifactId>
                <version>${hibernate.version}</version>
            </annotationProcessorPath>
        </annotationProcessorPaths>
    </configuration>
</plugin>

ตอนนี้เมื่อคอมไพล์โปรเจ็กต์คุณจะเห็นว่าในtargetโฟลเดอร์คลาส Java ต่อไปนี้ถูกสร้างขึ้น:

> tree target/generated-sources/
target/generated-sources/
└── annotations
    └── com
        └── vladmihalcea
            └── book
                └── hpjp
                    └── hibernate
                        ├── forum
                        │   ├── PostComment_.java
                        │   ├── PostDetails_.java
                        │   ├── Post_.java
                        │   └── Tag_.java

Metamodel เอนทิตีแท็ก

หากTagแมปเอนทิตีดังต่อไปนี้:

@Entity
@Table(name = "tag")
public class Tag {

    @Id
    private Long id;

    private String name;

    //Getters and setters omitted for brevity
}

Tag_ระดับ Metamodel ถูกสร้างขึ้นเช่นนี้

@Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor")
@StaticMetamodel(Tag.class)
public abstract class Tag_ {

    public static volatile SingularAttribute<Tag, String> name;
    public static volatile SingularAttribute<Tag, Long> id;

    public static final String NAME = "name";
    public static final String ID = "id";
}

SingularAttributeใช้สำหรับขั้นพื้นฐานidและname Tagคุณลักษณะนิติบุคคล JPA

โพสต์ Metamodel เอนทิตี

Postนิติบุคคลถูกแมปเช่นนี้

@Entity
@Table(name = "post")
public class Post {

    @Id
    private Long id;

    private String title;

    @OneToMany(
        mappedBy = "post",
        cascade = CascadeType.ALL,
        orphanRemoval = true
    )
    private List<PostComment> comments = new ArrayList<>();

    @OneToOne(
        mappedBy = "post",
        cascade = CascadeType.ALL,
        fetch = FetchType.LAZY
    )
    @LazyToOne(LazyToOneOption.NO_PROXY)
    private PostDetails details;

    @ManyToMany
    @JoinTable(
        name = "post_tag",
        joinColumns = @JoinColumn(name = "post_id"),
        inverseJoinColumns = @JoinColumn(name = "tag_id")
    )
    private List<Tag> tags = new ArrayList<>();

    //Getters and setters omitted for brevity
}

Postนิติบุคคลมีสองคุณลักษณะพื้นฐานidและtitleเป็นหนึ่งต่อหลายคนcommentsคอลเลกชันหนึ่งต่อหนึ่งdetailsสมาคมและหลายต่อหลายtagsคอลเลกชัน

Post_ระดับ Metamodel ถูกสร้างขึ้นดังต่อไปนี้:

@Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor")
@StaticMetamodel(Post.class)
public abstract class Post_ {

    public static volatile ListAttribute<Post, PostComment> comments;
    public static volatile SingularAttribute<Post, PostDetails> details;
    public static volatile SingularAttribute<Post, Long> id;
    public static volatile SingularAttribute<Post, String> title;
    public static volatile ListAttribute<Post, Tag> tags;

    public static final String COMMENTS = "comments";
    public static final String DETAILS = "details";
    public static final String ID = "id";
    public static final String TITLE = "title";
    public static final String TAGS = "tags";
}

พื้นฐานidและtitleแอตทริบิวต์ตลอดจนการdetailsเชื่อมโยงแบบหนึ่งต่อหนึ่งจะแสดงโดยSingularAttributeในขณะที่commentsและtagsคอลเลกชันโดยมีตัวแทน ListAttributeJPA

PostDetails entity Metamodel

PostDetailsนิติบุคคลถูกแมปเช่นนี้

@Entity
@Table(name = "post_details")
public class PostDetails {

    @Id
    @GeneratedValue
    private Long id;

    @Column(name = "created_on")
    private Date createdOn;

    @Column(name = "created_by")
    private String createdBy;

    @OneToOne(fetch = FetchType.LAZY)
    @MapsId
    @JoinColumn(name = "id")
    private Post post;

    //Getters and setters omitted for brevity
}

แอตทริบิวต์เอนทิตีทั้งหมดจะแสดงโดย JPA SingularAttributeในPostDetails_คลาส Metamodel ที่เกี่ยวข้อง:

@Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor")
@StaticMetamodel(PostDetails.class)
public abstract class PostDetails_ {

    public static volatile SingularAttribute<PostDetails, Post> post;
    public static volatile SingularAttribute<PostDetails, String> createdBy;
    public static volatile SingularAttribute<PostDetails, Long> id;
    public static volatile SingularAttribute<PostDetails, Date> createdOn;

    public static final String POST = "post";
    public static final String CREATED_BY = "createdBy";
    public static final String ID = "id";
    public static final String CREATED_ON = "createdOn";
}

PostComment entity Metamodel

PostCommentถูกแมปดังต่อไปนี้:

@Entity
@Table(name = "post_comment")
public class PostComment {

    @Id
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    private Post post;

    private String review;

    //Getters and setters omitted for brevity
}

และแอตทริบิวต์เอนทิตีทั้งหมดแสดงโดย JPA SingularAttributeในPostComments_คลาส Metamodel ที่เกี่ยวข้อง:

@Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor")
@StaticMetamodel(PostComment.class)
public abstract class PostComment_ {

    public static volatile SingularAttribute<PostComment, Post> post;
    public static volatile SingularAttribute<PostComment, String> review;
    public static volatile SingularAttribute<PostComment, Long> id;

    public static final String POST = "post";
    public static final String REVIEW = "review";
    public static final String ID = "id";
}

การใช้ JPA Criteria Metamodel

หากไม่มี JPA Metamodel แบบสอบถาม Criteria API ที่ต้องการดึงข้อมูลPostCommentเอนทิตีที่กรองด้วยPostชื่อที่เกี่ยวข้องจะมีลักษณะดังนี้:

CriteriaBuilder builder = entityManager.getCriteriaBuilder();

CriteriaQuery<PostComment> query = builder.createQuery(PostComment.class);
Root<PostComment> postComment = query.from(PostComment.class);

Join<PostComment, Post> post = postComment.join("post");

query.where(
    builder.equal(
        post.get("title"),
        "High-Performance Java Persistence"
    )
);

List<PostComment> comments = entityManager
    .createQuery(query)
    .getResultList();

สังเกตว่าเราใช้postString literal เมื่อสร้างJoinอินสแตนซ์และเราใช้titleString literal เมื่ออ้างถึงไฟล์Post title.

JPA Metamodel ช่วยให้เราสามารถหลีกเลี่ยงแอตทริบิวต์ของเอนทิตีที่เข้ารหัสยากดังตัวอย่างต่อไปนี้:

CriteriaBuilder builder = entityManager.getCriteriaBuilder();

CriteriaQuery<PostComment> query = builder.createQuery(PostComment.class);
Root<PostComment> postComment = query.from(PostComment.class);

Join<PostComment, Post> post = postComment.join(PostComment_.post);

query.where(
    builder.equal(
        post.get(Post_.title),
        "High-Performance Java Persistence"
    )
);

List<PostComment> comments = entityManager
    .createQuery(query)
    .getResultList();

การเขียนแบบสอบถาม JPA Criteria API นั้นง่ายกว่ามากหากคุณใช้เครื่องมือเติมโค้ดเช่น Codota อ่านบทความนี้เพื่อดูรายละเอียดเพิ่มเติมเกี่ยวกับปลั๊กอิน Codota IDE

หรือสมมติว่าเราต้องการดึงการฉาย DTOในขณะที่กรองPost titleและPostDetails createdOnแอตทริบิวต์

เราสามารถใช้ Metamodel เมื่อสร้างแอตทริบิวต์ join เช่นเดียวกับเมื่อสร้างนามแฝงคอลัมน์การฉาย DTO หรือเมื่ออ้างอิงแอตทริบิวต์เอนทิตีที่เราต้องการกรอง:

CriteriaBuilder builder = entityManager.getCriteriaBuilder();

CriteriaQuery<Object[]> query = builder.createQuery(Object[].class);

Root<PostComment> postComment = query.from(PostComment.class);
Join<PostComment, Post> post = postComment.join(PostComment_.post);

query.multiselect(
    postComment.get(PostComment_.id).alias(PostComment_.ID),
    postComment.get(PostComment_.review).alias(PostComment_.REVIEW),
    post.get(Post_.title).alias(Post_.TITLE)
);

query.where(
    builder.and(
        builder.like(
            post.get(Post_.title),
            "%Java Persistence%"
        ),
        builder.equal(
            post.get(Post_.details).get(PostDetails_.CREATED_BY),
            "Vlad Mihalcea"
        )
    )
);

List<PostCommentSummary> comments = entityManager
    .createQuery(query)
    .unwrap(Query.class)
    .setResultTransformer(Transformers.aliasToBean(PostCommentSummary.class))
    .getResultList();

เจ๋งใช่มั้ย?


0

โอเคตามสิ่งที่ฉันได้อ่านที่นี่ฉันทำกับEclipseLinkด้วยวิธีนี้และฉันไม่จำเป็นต้องใส่การพึ่งพาโปรเซสเซอร์ให้กับโปรเจ็กต์เพียง แต่เป็นannotationProcessorPathองค์ประกอบของปลั๊กอินคอมไพเลอร์

    <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
            <annotationProcessorPaths>
                <annotationProcessorPath>
                    <groupId>org.eclipse.persistence</groupId>
                    <artifactId>org.eclipse.persistence.jpa.modelgen.processor</artifactId>
                    <version>2.7.7</version>
                </annotationProcessorPath>
            </annotationProcessorPaths>
            <compilerArgs>
                <arg>-Aeclipselink.persistencexml=src/main/resources/META-INF/persistence.xml</arg>
            </compilerArgs>
        </configuration>
    </plugin>
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.