├── nb-configuration.xml ├── pom.xml └── src ├── main ├── java │ └── org │ │ └── thoughts │ │ └── on │ │ └── java │ │ └── jpa │ │ ├── model │ │ ├── Author.java │ │ └── Book.java │ │ └── value │ │ └── BookValue.java └── resources │ └── META-INF │ ├── orm.xml │ └── persistence.xml └── test ├── java └── org │ └── thoughts │ └── on │ └── java │ └── jpa │ └── model │ └── TestResultMapping.java └── resources └── data └── testData.yml /nb-configuration.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | 16 | JDK_1.8 17 | 18 | 19 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | org.thoughts.on.java.jpa 6 | ResultMapping 7 | 1.0.0-SNAPSHOT 8 | ResultMapping 9 | 10 | 1.8 11 | 1.8 12 | UTF-8 13 | 14 | 1.1.7.Final 15 | 1.0.0.Alpha7 16 | 4.11 17 | 8.2.0.Final 18 | 19 | 20 | 21 | 22 | org.jboss.spec 23 | jboss-javaee-all-7.0 24 | 1.0.2.Final 25 | pom 26 | import 27 | 28 | 29 | org.jboss.arquillian 30 | arquillian-bom 31 | ${version.arquillian_core} 32 | pom 33 | import 34 | 35 | 36 | 37 | 38 | 39 | org.hibernate.javax.persistence 40 | hibernate-jpa-2.1-api 41 | provided 42 | 43 | 44 | junit 45 | junit 46 | ${version.junit} 47 | test 48 | 49 | 50 | org.jboss.arquillian.junit 51 | arquillian-junit-container 52 | test 53 | 54 | 55 | org.jboss.arquillian.extension 56 | arquillian-persistence-dbunit 57 | ${version.arquillian_persistence} 58 | test 59 | 60 | 61 | 62 | ResultMapping 63 | 64 | 65 | maven-compiler-plugin 66 | 3.3 67 | 68 | ${maven.compiler.source} 69 | ${maven.compiler.target} 70 | ${project.build.sourceEncoding} 71 | 72 | 73 | 74 | maven-surefire-plugin 75 | 2.17 76 | 77 | 78 | 79 | 80 | 81 | JBOSS_NEXUS 82 | http://repository.jboss.org/nexus/content/groups/public 83 | 84 | 85 | 86 | 87 | arq-wildfly-managed 88 | 89 | true 90 | 91 | 92 | 93 | 94 | org.apache.maven.plugins 95 | maven-dependency-plugin 96 | 97 | 98 | unpack 99 | process-test-classes 100 | 101 | unpack 102 | 103 | 104 | 105 | 106 | org.wildfly 107 | wildfly-dist 108 | ${version.wildfly} 109 | zip 110 | false 111 | ${project.build.directory} 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | maven-surefire-plugin 120 | 121 | 122 | ${project.build.directory}/wildfly-${version.wildfly} 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | org.wildfly 131 | wildfly-arquillian-container-managed 132 | ${version.wildfly} 133 | test 134 | 135 | 136 | 137 | 138 | arq-wildfly-remote 139 | 140 | 141 | org.wildfly 142 | wildfly-arquillian-container-remote 143 | ${version.wildfly} 144 | test 145 | 146 | 147 | 148 | 149 | 150 | -------------------------------------------------------------------------------- /src/main/java/org/thoughts/on/java/jpa/model/Author.java: -------------------------------------------------------------------------------- 1 | package org.thoughts.on.java.jpa.model; 2 | 3 | import javax.persistence.Entity; 4 | import java.io.Serializable; 5 | import javax.persistence.Id; 6 | import javax.persistence.GeneratedValue; 7 | import javax.persistence.GenerationType; 8 | import javax.persistence.Column; 9 | import javax.persistence.Version; 10 | import java.lang.Override; 11 | import javax.persistence.ColumnResult; 12 | import javax.persistence.ConstructorResult; 13 | import javax.persistence.EntityResult; 14 | import javax.persistence.FieldResult; 15 | import javax.persistence.SqlResultSetMapping; 16 | import javax.persistence.SqlResultSetMappings; 17 | import org.thoughts.on.java.jpa.value.BookValue; 18 | 19 | @Entity 20 | @SqlResultSetMappings({ 21 | @SqlResultSetMapping( 22 | name = "BookAuthorMapping", 23 | entities = { 24 | @EntityResult( 25 | entityClass = Book.class, 26 | fields = { 27 | @FieldResult(name = "id", column = "id"), 28 | @FieldResult(name = "title", column = "title"), 29 | @FieldResult(name = "author", column = "author_id"), 30 | @FieldResult(name = "version", column = "version")}), 31 | @EntityResult( 32 | entityClass = Author.class, 33 | fields = { 34 | @FieldResult(name = "id", column = "authorId"), 35 | @FieldResult(name = "firstName", column = "firstName"), 36 | @FieldResult(name = "lastName", column = "lastName"), 37 | @FieldResult(name = "version", column = "authorVersion")})}), 38 | @SqlResultSetMapping( 39 | name = "AuthorMapping", 40 | entities = @EntityResult( 41 | entityClass = Author.class, 42 | fields = { 43 | @FieldResult(name = "id", column = "authorId"), 44 | @FieldResult(name = "firstName", column = "firstName"), 45 | @FieldResult(name = "lastName", column = "lastName"), 46 | @FieldResult(name = "version", column = "version")})), 47 | @SqlResultSetMapping( 48 | name = "AuthorBookCountMapping", 49 | entities = @EntityResult( 50 | entityClass = Author.class, 51 | fields = { 52 | @FieldResult(name = "id", column = "id"), 53 | @FieldResult(name = "firstName", column = "firstName"), 54 | @FieldResult(name = "lastName", column = "lastName"), 55 | @FieldResult(name = "version", column = "version")}), 56 | columns = @ColumnResult(name = "bookCount", type = Long.class)), 57 | @SqlResultSetMapping( 58 | name = "BookValueMapping", 59 | classes = @ConstructorResult( 60 | targetClass = BookValue.class, 61 | columns = { 62 | @ColumnResult(name = "id", type = Long.class), 63 | @ColumnResult(name = "title"), 64 | @ColumnResult(name = "version", type = Long.class), 65 | @ColumnResult(name = "authorName")})), 66 | @SqlResultSetMapping( 67 | name = "BookValueAndEntityMapping", 68 | entities = { 69 | @EntityResult( 70 | entityClass = Book.class, 71 | fields = { 72 | @FieldResult(name = "id", column = "id"), 73 | @FieldResult(name = "title", column = "title"), 74 | @FieldResult(name = "author", column = "author_id"), 75 | @FieldResult(name = "version", column = "version")})}, 76 | classes = @ConstructorResult( 77 | targetClass = BookValue.class, 78 | columns = { 79 | @ColumnResult(name = "id", type = Long.class), 80 | @ColumnResult(name = "title"), 81 | @ColumnResult(name = "version", type = Long.class), 82 | @ColumnResult(name = "authorName")})) 83 | }) 84 | public class Author implements Serializable { 85 | 86 | @Id 87 | @GeneratedValue(strategy = GenerationType.AUTO) 88 | @Column(name = "id", updatable = false, nullable = false) 89 | private Long id; 90 | @Version 91 | @Column(name = "version") 92 | private int version; 93 | 94 | @Column 95 | private String firstName; 96 | 97 | @Column 98 | private String lastName; 99 | 100 | public Long getId() { 101 | return this.id; 102 | } 103 | 104 | public void setId(final Long id) { 105 | this.id = id; 106 | } 107 | 108 | public int getVersion() { 109 | return this.version; 110 | } 111 | 112 | public void setVersion(final int version) { 113 | this.version = version; 114 | } 115 | 116 | @Override 117 | public boolean equals(Object obj) { 118 | if (this == obj) { 119 | return true; 120 | } 121 | if (!(obj instanceof Author)) { 122 | return false; 123 | } 124 | Author other = (Author) obj; 125 | if (id != null) { 126 | if (!id.equals(other.id)) { 127 | return false; 128 | } 129 | } 130 | return true; 131 | } 132 | 133 | @Override 134 | public int hashCode() { 135 | final int prime = 31; 136 | int result = 1; 137 | result = prime * result + ((id == null) ? 0 : id.hashCode()); 138 | return result; 139 | } 140 | 141 | public String getFirstName() { 142 | return firstName; 143 | } 144 | 145 | public void setFirstName(String firstName) { 146 | this.firstName = firstName; 147 | } 148 | 149 | public String getLastName() { 150 | return lastName; 151 | } 152 | 153 | public void setLastName(String lastName) { 154 | this.lastName = lastName; 155 | } 156 | 157 | @Override 158 | public String toString() { 159 | String result = getClass().getSimpleName() + " "; 160 | if (firstName != null && !firstName.trim().isEmpty()) { 161 | result += "firstName: " + firstName; 162 | } 163 | if (lastName != null && !lastName.trim().isEmpty()) { 164 | result += ", lastName: " + lastName; 165 | } 166 | return result; 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /src/main/java/org/thoughts/on/java/jpa/model/Book.java: -------------------------------------------------------------------------------- 1 | package org.thoughts.on.java.jpa.model; 2 | 3 | import javax.persistence.Entity; 4 | import java.io.Serializable; 5 | import javax.persistence.Id; 6 | import javax.persistence.GeneratedValue; 7 | import javax.persistence.GenerationType; 8 | import javax.persistence.Column; 9 | import javax.persistence.Version; 10 | import java.lang.Override; 11 | import javax.persistence.ManyToOne; 12 | import javax.persistence.FetchType; 13 | 14 | @Entity 15 | public class Book implements Serializable { 16 | 17 | @Id 18 | @GeneratedValue(strategy = GenerationType.AUTO) 19 | @Column(name = "id", updatable = false, nullable = false) 20 | private Long id; 21 | @Version 22 | @Column(name = "version") 23 | private int version; 24 | 25 | @Column 26 | private String title; 27 | 28 | @ManyToOne(fetch = FetchType.LAZY) 29 | private Author author; 30 | 31 | public Long getId() { 32 | return this.id; 33 | } 34 | 35 | public void setId(final Long id) { 36 | this.id = id; 37 | } 38 | 39 | public int getVersion() { 40 | return this.version; 41 | } 42 | 43 | public void setVersion(final int version) { 44 | this.version = version; 45 | } 46 | 47 | public String getTitle() { 48 | return title; 49 | } 50 | 51 | public void setTitle(String title) { 52 | this.title = title; 53 | } 54 | 55 | public Author getAuthor() { 56 | return this.author; 57 | } 58 | 59 | public void setAuthor(final Author author) { 60 | this.author = author; 61 | } 62 | 63 | @Override 64 | public String toString() { 65 | String result = getClass().getSimpleName() + " "; 66 | if (title != null && !title.trim().isEmpty()) { 67 | result += "title: " + title; 68 | } 69 | return result; 70 | } 71 | 72 | @Override 73 | public boolean equals(Object obj) { 74 | if (this == obj) { 75 | return true; 76 | } 77 | if (!(obj instanceof Book)) { 78 | return false; 79 | } 80 | Book other = (Book) obj; 81 | if (id != null) { 82 | if (!id.equals(other.id)) { 83 | return false; 84 | } 85 | } 86 | return true; 87 | } 88 | 89 | @Override 90 | public int hashCode() { 91 | final int prime = 31; 92 | int result = 1; 93 | result = prime * result + ((id == null) ? 0 : id.hashCode()); 94 | return result; 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /src/main/java/org/thoughts/on/java/jpa/value/BookValue.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package org.thoughts.on.java.jpa.value; 7 | 8 | /** 9 | * 10 | * @author Thorben 11 | */ 12 | public class BookValue { 13 | 14 | private Long id; 15 | private String title; 16 | private Long version; 17 | private String authorName; 18 | 19 | public BookValue(Long id, String title, Long version, String authorName) { 20 | this.id = id; 21 | this.title = title; 22 | this.version = version; 23 | this.authorName = authorName; 24 | } 25 | 26 | public Long getId() { 27 | return id; 28 | } 29 | 30 | public void setId(Long id) { 31 | this.id = id; 32 | } 33 | 34 | public String getTitle() { 35 | return title; 36 | } 37 | 38 | public void setTitle(String title) { 39 | this.title = title; 40 | } 41 | 42 | public Long getVersion() { 43 | return version; 44 | } 45 | 46 | public void setVersion(Long version) { 47 | this.version = version; 48 | } 49 | 50 | public String getAuthorName() { 51 | return authorName; 52 | } 53 | 54 | public void setAuthorName(String authorName) { 55 | this.authorName = authorName; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/orm.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/persistence.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Forge Persistence Unit 5 | org.hibernate.ejb.HibernatePersistence 6 | java:jboss/datasources/ExampleDS 7 | false 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/test/java/org/thoughts/on/java/jpa/model/TestResultMapping.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package org.thoughts.on.java.jpa.model; 7 | 8 | import java.math.BigInteger; 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | import javax.persistence.EntityManager; 12 | import javax.persistence.PersistenceContext; 13 | import javax.persistence.Query; 14 | import org.jboss.arquillian.container.test.api.Deployment; 15 | import org.jboss.arquillian.junit.Arquillian; 16 | import org.jboss.arquillian.persistence.UsingDataSet; 17 | import org.jboss.shrinkwrap.api.ShrinkWrap; 18 | import org.jboss.shrinkwrap.api.asset.EmptyAsset; 19 | import org.jboss.shrinkwrap.api.spec.JavaArchive; 20 | import org.junit.Test; 21 | import org.junit.runner.RunWith; 22 | import org.thoughts.on.java.jpa.value.BookValue; 23 | 24 | /** 25 | * 26 | * @author Thorben 27 | */ 28 | @RunWith(Arquillian.class) 29 | public class TestResultMapping { 30 | 31 | @PersistenceContext 32 | private EntityManager em; 33 | 34 | @Deployment 35 | public static JavaArchive createDeployment() { 36 | return ShrinkWrap 37 | .create(JavaArchive.class) 38 | .addClasses(Author.class, Book.class, BookValue.class) 39 | .addAsManifestResource("META-INF/persistence.xml", 40 | "persistence.xml") 41 | .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml") 42 | .addAsManifestResource("META-INF/orm.xml", 43 | "orm.xml"); 44 | } 45 | 46 | @Test 47 | @UsingDataSet("data/testData.yml") 48 | public void queryWithoutMapping() { 49 | List results = this.em.createNativeQuery("SELECT a.id, a.firstName, a.lastName, a.version FROM Author a").getResultList(); 50 | results.stream().forEach((record) -> { 51 | Long id = ((BigInteger) record[0]).longValue(); 52 | String firstName = (String) record[1]; 53 | String lastName = (String) record[2]; 54 | Integer version = (Integer) record[3]; 55 | }); 56 | } 57 | 58 | @Test 59 | @UsingDataSet("data/testData.yml") 60 | public void queryWithAuthorEntityDefaultMapping() { 61 | List results = this.em.createNativeQuery("SELECT a.id, a.firstName, a.lastName, a.version FROM Author a", Author.class).getResultList(); 62 | results.stream().forEach((author) -> { 63 | System.out.println("Author: ID [" + author.getId() + "] firstName [" + author.getFirstName() + "] lastName [" + author.getLastName() + "]"); 64 | }); 65 | } 66 | 67 | @Test 68 | @UsingDataSet("data/testData.yml") 69 | public void queryWithAuthorEntityCustomMapping() { 70 | List results = this.em.createNativeQuery("SELECT a.id as authorId, a.firstName, a.lastName, a.version FROM Author a", "AuthorMapping").getResultList(); 71 | results.stream().forEach((author) -> { 72 | System.out.println("Author: ID [" + author.getId() + "] firstName [" + author.getFirstName() + "] lastName [" + author.getLastName() + "]"); 73 | }); 74 | } 75 | 76 | @Test 77 | @UsingDataSet("data/testData.yml") 78 | public void queryWithAuthorEntityCustomXmlMapping() { 79 | List results = this.em.createNativeQuery("SELECT a.id as authorId, a.firstName, a.lastName, a.version FROM Author a", "AuthorMappingXml").getResultList(); 80 | results.stream().forEach((author) -> { 81 | System.out.println("Author: ID [" + author.getId() + "] firstName [" + author.getFirstName() + "] lastName [" + author.getLastName() + "]"); 82 | }); 83 | } 84 | 85 | @Test 86 | @UsingDataSet("data/testData.yml") 87 | public void queryWithAuthorBookEntityMapping() { 88 | List results = this.em.createNativeQuery("SELECT b.id, b.title, b.author_id, b.version, a.id as authorId, a.firstName, a.lastName, a.version as authorVersion FROM Book b JOIN Author a ON b.author_id = a.id", "BookAuthorMapping").getResultList(); 89 | results.stream().forEach((record) -> { 90 | Book book = (Book) record[0]; 91 | Author author = (Author) record[1]; 92 | System.out.println("Author: ID [" + author.getId() + "] firstName [" + author.getFirstName() + "] lastName [" + author.getLastName() + "]"); 93 | System.out.println("Book: ID [" + book.getId() + "] title[" + book.getTitle() + "] author [" + book.getAuthor().getFirstName() + " " + book.getAuthor().getLastName() + "]"); 94 | }); 95 | } 96 | 97 | @Test 98 | @UsingDataSet("data/testData.yml") 99 | public void queryWithAuthorBookEntityXmlMapping() { 100 | List results = this.em.createNativeQuery("SELECT b.id, b.title, b.author_id, b.version, a.id as authorId, a.firstName, a.lastName, a.version as authorVersion FROM Book b JOIN Author a ON b.author_id = a.id", "BookAuthorMappingXml").getResultList(); 101 | results.stream().forEach((record) -> { 102 | Book book = (Book) record[1]; 103 | Author author = (Author) record[0]; 104 | System.out.println("Author: ID [" + author.getId() + "] firstName [" + author.getFirstName() + "] lastName [" + author.getLastName() + "]"); 105 | System.out.println("Book: ID [" + book.getId() + "] title[" + book.getTitle() + "]"); 106 | }); 107 | } 108 | 109 | @Test 110 | @UsingDataSet("data/testData.yml") 111 | public void queryWithAuthorBookCountMapping() { 112 | List results = this.em.createNativeQuery("SELECT a.id, a.firstName, a.lastName, a.version, count(b.id) as bookCount FROM Book b JOIN Author a ON b.author_id = a.id GROUP BY a.id, a.firstName, a.lastName, a.version", "AuthorBookCountMapping").getResultList(); 113 | results.stream().forEach((record) -> { 114 | Author author = (Author) record[0]; 115 | Long bookCount = (Long) record[1]; 116 | System.out.println("Author: ID [" + author.getId() + "] firstName [" + author.getFirstName() + "] lastName [" + author.getLastName() + "] number of books [" + bookCount + "]"); 117 | }); 118 | } 119 | 120 | @Test 121 | @UsingDataSet("data/testData.yml") 122 | public void queryWithAuthorBookCountXmlMapping() { 123 | List results = this.em.createNativeQuery("SELECT a.id, a.firstName, a.lastName, a.version, count(b.id) as bookCount FROM Book b JOIN Author a ON b.author_id = a.id GROUP BY a.id, a.firstName, a.lastName, a.version", "AuthorBookCountMappingXml").getResultList(); 124 | results.stream().forEach((record) -> { 125 | Author author = (Author) record[0]; 126 | Long bookCount = (Long) record[1]; 127 | System.out.println("Author: ID [" + author.getId() + "] firstName [" + author.getFirstName() + "] lastName [" + author.getLastName() + "] number of books [" + bookCount + "]"); 128 | }); 129 | } 130 | 131 | @Test 132 | @UsingDataSet("data/testData.yml") 133 | public void queryWithBookValueMapping() { 134 | List results = this.em.createNativeQuery("SELECT b.id, b.title, b.version, a.firstName || a.lastName as authorName FROM Book b JOIN Author a ON b.author_id = a.id", "BookValueMapping").getResultList(); 135 | results.stream().forEach((book) -> {; 136 | System.out.println("Book: ID [" + book.getId() + "] title [" + book.getTitle() + "] authorName [" + book.getAuthorName() + "]"); 137 | }); 138 | } 139 | 140 | @Test 141 | @UsingDataSet("data/testData.yml") 142 | public void queryWithBookValueAndEntityMapping() { 143 | List results = this.em.createNativeQuery("SELECT b.id, b.title, b.version, a.firstName || a.lastName as authorName, b.author_id FROM Book b JOIN Author a ON b.author_id = a.id", "BookValueAndEntityMapping").getResultList(); 144 | results.stream().forEach((Object[] record) -> { 145 | Book entity = (Book) record[0]; 146 | BookValue book = (BookValue) record[1]; 147 | System.out.println(entity); 148 | System.out.println("Book: ID [" + book.getId() + "] title [" + book.getTitle() + "] authorName [" + book.getAuthorName() + "]"); 149 | }); 150 | } 151 | 152 | @Test 153 | @UsingDataSet("data/testData.yml") 154 | public void queryWithBookValueXmlMapping() { 155 | List results = this.em.createNativeQuery("SELECT b.id, b.title, b.version, a.firstName || a.lastName as authorName FROM Book b JOIN Author a ON b.author_id = a.id", "BookValueMappingXml").getResultList(); 156 | results.stream().forEach((book) -> { 157 | System.out.println("Book: ID [" + book.getId() + "] title [" + book.getTitle() + "] authorName [" + book.getAuthorName() + "]"); 158 | }); 159 | } 160 | 161 | @Test 162 | @UsingDataSet("data/testData.yml") 163 | public void queryWithBookValueStreamMapping() { 164 | List results = this.em.createNativeQuery("SELECT b.id, b.title, b.version, a.firstName || a.lastName as authorName FROM Book b JOIN Author a ON b.author_id = a.id").getResultList(); 165 | List books = new ArrayList<>(results.size()); 166 | results.stream().forEach((record) -> books.add(new BookValue(((BigInteger) record[0]).longValue(), (String) record[1], ((Integer) record[2]).longValue(), (String) record[3]))); 167 | 168 | books.stream().forEach((book) -> { 169 | System.out.println("Book: ID [" + book.getId() + "] title [" + book.getTitle() + "] authorName [" + book.getAuthorName() + "]"); 170 | }); 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /src/test/resources/data/testData.yml: -------------------------------------------------------------------------------- 1 | Author: 2 | - id: 1 3 | firstName: Arthur Conan 4 | lastName: Doyle 5 | version: 0 6 | - id: 2 7 | firstName: John Ronald Reuel 8 | lastName: Tolkien 9 | version: 0 10 | 11 | Book: 12 | - id: 1 13 | title: The Yellow Face 14 | author_id: 1 15 | version: 0 16 | - id: 2 17 | title: The Empty House 18 | author_id: 1 19 | version: 0 20 | - id: 3 21 | title: The Lord of the Rings 22 | author_id: 2 23 | version: 0 24 | - id: 4 25 | title: The Hobbit 26 | author_id: 2 27 | version: 0 --------------------------------------------------------------------------------