diff --git a/src/protean/core/entity.py b/src/protean/core/entity.py
index 46e08f9d..b8c625f1 100644
--- a/src/protean/core/entity.py
+++ b/src/protean/core/entity.py
@@ -234,6 +234,25 @@ class Entity(metaclass=EntityBase):
         # Return the first result
         return results.first
 
+    @classmethod
+    def find_by(cls, **kwargs) -> 'Entity':
+        """Find a specific entity record that matches one or more criteria.
+
+        :param kwargs: named arguments consisting of attr_name and attr_value pairs to search on
+        """
+        logger.debug(f'Lookup `{cls.__name__}` object with values '
+                     f'{kwargs}')
+
+        # Find this item in the repository or raise Error
+        results = cls.filter(page=1, per_page=1, **kwargs)
+        if not results:
+            raise ObjectNotFoundError(
+                f'`{cls.__name__}` object with values {[item for item in kwargs.items()]} '
+                f'does not exist.')
+
+        # Return the first result
+        return results.first
+
     @classmethod
     def _retrieve_model(cls):
         """Retrieve model details associated with this Entity"""
@@ -315,6 +334,7 @@ class Entity(metaclass=EntityBase):
         model_cls, adapter = cls._retrieve_model()
 
         # Build the entity from the input arguments
+        # Raises validation errors, if any, at this point
         entity = cls(*args, **kwargs)
 
         # Do unique checks, create this object and return it
@@ -334,6 +354,21 @@ class Entity(metaclass=EntityBase):
 
         return entity
 
+    def save(self):
+        """Save a new Entity into repository.
+
+        Performs unique validations before creating the entity.
+        """
+        logger.debug(
+            f'Creating new `{self.__class__.__name__}` object')
+
+        values = {}
+        for item in self.meta_.declared_fields.items():
+            values[item[0]] = getattr(self, item[0])
+
+        return self.__class__.create(**values)
+
+
     def update(self, *data, **kwargs) -> 'Entity':
         """Update a Record in the repository.
 

Introduce `save()` to persist current entity state to repository
Allowing `save()` to be called on a loaded entity will enable us to persist all fields that may have changed in the object. Typically used when the changes to an entity span across multiple attributes or values to be updated are not straightforward and involve a calculation.
diff --git a/tests/core/test_entity.py b/tests/core/test_entity.py
index f12f9be8..2bf05298 100644
--- a/tests/core/test_entity.py
+++ b/tests/core/test_entity.py
@@ -114,6 +114,57 @@ class TestEntity:
         assert dog.to_dict() == {
             'age': 10, 'id': 1, 'name': 'John Doe', 'owner': 'Jimmy'}
 
+    def test_get(self):
+        """Test Entity Retrieval by its primary key"""
+        Dog.create(id=1234, name='Johnny', owner='John')
+
+        dog = Dog.get(1234)
+        assert dog is not None
+        assert dog.id == 1234
+
+    def test_get_object_not_found_error(self):
+        """Test failed Entity Retrieval by its primary key"""
+        Dog.create(id=1234, name='Johnny', owner='John')
+
+        with pytest.raises(ObjectNotFoundError):
+            Dog.get(1235)
+
+    def test_find_by(self):
+        """Test Entity retrieval by a specific column's value"""
+
+        Dog.create(id=2345, name='Johnny', owner='John')
+
+        dog = Dog.find_by(name='Johnny')
+        assert dog is not None
+        assert dog.id == 2345
+
+    def test_find_by_object_not_found_error(self):
+        """Test Entity retrieval by specific column value(s)"""
+
+        Dog.create(id=2345, name='Johnny', owner='John')
+
+        with pytest.raises(ObjectNotFoundError):
+            Dog.find_by(name='JohnnyChase')
+
+    def test_find_by_multiple_attributes(self):
+        """Test Entity retrieval by multiple column value(s)"""
+
+        Dog.create(id=2346, name='Johnny1', age=8, owner='John')
+        Dog.create(id=2347, name='Johnny2', age=6, owner='John')
+
+        dog = Dog.find_by(name='Johnny1', age=8)
+        assert dog is not None
+        assert dog.id == 2346
+
+    def test_find_by_multiple_attributes_object_not_found_error(self):
+        """Test Entity retrieval by multiple column value(s)"""
+
+        Dog.create(id=2346, name='Johnny1', age=8, owner='John')
+        Dog.create(id=2347, name='Johnny2', age=6, owner='John')
+
+        with pytest.raises(ObjectNotFoundError):
+            Dog.find_by(name='Johnny1', age=6)
+
     def test_create_error(self):
         """ Add an entity to the repository missing a required attribute"""
         with pytest.raises(ValidationError):
@@ -131,6 +182,25 @@ class TestEntity:
         dog = Dog.get(11344234)
         assert dog is not None
 
+    def test_save(self):
+        """Initialize an entity and save it to repository"""
+        dog = Dog(name='Johnny', owner='John')
+
+        saved_dog = dog.save()
+        assert saved_dog is not None
+        assert saved_dog.id is not None
+        assert saved_dog.name == 'Johnny'
+        assert saved_dog.age == 5
+        assert saved_dog.owner == 'John'
+
+    def test_save_validation_error(self):
+        """Test failed `save()` because of validation errors"""
+        dog = Dog(name='Johnny', owner='John')
+
+        with pytest.raises(ValidationError):
+            del dog.name  # Simulate an error by force-deleting an attribute
+            dog.save()
+
     def test_update_with_invalid_id(self):
         """Try to update a non-existing entry"""
 

pip install -e .[dev]
parse_log_pytest
requirements.txt
pytest
3.6
requirements.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
alabaster==0.7.13
astroid==2.11.7
attrs==22.2.0
Babel==2.11.0
certifi==2021.5.30
charset-normalizer==2.0.12
check-manifest==0.37
Click==7.0
coverage==4.5.1
distlib==0.3.9
docutils==0.14
filelock==3.4.1
flake8==3.5.0
idna==3.10
imagesize==1.4.1
importlib-metadata==4.8.3
importlib-resources==5.4.0
iniconfig==1.1.1
isort==5.10.1
Jinja2==3.0.3
lazy-object-proxy==1.7.1
MarkupSafe==2.0.1
mccabe==0.6.1
packaging==21.3
pkginfo==1.10.0
platformdirs==2.4.0
pluggy==0.13.1
-e git+https://github.com/proteanhq/protean.git@328421caaf51e0fd559d0570ea51f7c42ee70346#egg=protean
py==1.11.0
pycodestyle==2.3.1
pyflakes==1.6.0
Pygments==2.14.0
pylint==2.0.0
pyparsing==3.1.4
pytest==7.0.1
python-dateutil==2.7.3
pytz==2025.2
requests==2.27.1
requests-toolbelt==1.0.0
six==1.17.0
snowballstemmer==2.2.0
Sphinx==1.8.3
sphinxcontrib-serializinghtml==1.1.5
sphinxcontrib-websupport==1.2.4
tomli==1.2.3
tox==3.1.2
tqdm==4.64.1
twine==1.11.0
typed-ast==1.5.5
typing_extensions==4.1.1
urllib3==1.26.20
virtualenv==20.17.1
wrapt==1.16.0
zipp==3.6.0

name: protean
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2021.5.30=py36h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.3=he6710b0_2
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - pip=21.2.2=py36h06a4308_0
  - python=3.6.13=h12debd9_1
  - readline=8.2=h5eee18b_0
  - setuptools=58.0.4=py36h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.37.1=pyhd3eb1b0_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - alabaster==0.7.13
      - astroid==2.11.7
      - attrs==22.2.0
      - babel==2.11.0
      - charset-normalizer==2.0.12
      - check-manifest==0.37
      - click==7.0
      - coverage==4.5.1
      - distlib==0.3.9
      - docutils==0.14
      - filelock==3.4.1
      - flake8==3.5.0
      - idna==3.10
      - imagesize==1.4.1
      - importlib-metadata==4.8.3
      - importlib-resources==5.4.0
      - iniconfig==1.1.1
      - isort==5.10.1
      - jinja2==3.0.3
      - lazy-object-proxy==1.7.1
      - markupsafe==2.0.1
      - mccabe==0.6.1
      - packaging==21.3
      - pkginfo==1.10.0
      - platformdirs==2.4.0
      - pluggy==0.13.1
      - py==1.11.0
      - pycodestyle==2.3.1
      - pyflakes==1.6.0
      - pygments==2.14.0
      - pylint==2.0.0
      - pyparsing==3.1.4
      - pytest==7.0.1
      - python-dateutil==2.7.3
      - pytz==2025.2
      - requests==2.27.1
      - requests-toolbelt==1.0.0
      - six==1.17.0
      - snowballstemmer==2.2.0
      - sphinx==1.8.3
      - sphinxcontrib-serializinghtml==1.1.5
      - sphinxcontrib-websupport==1.2.4
      - tomli==1.2.3
      - tox==3.1.2
      - tqdm==4.64.1
      - twine==1.11.0
      - typed-ast==1.5.5
      - typing-extensions==4.1.1
      - urllib3==1.26.20
      - virtualenv==20.17.1
      - wrapt==1.16.0
      - zipp==3.6.0
prefix: /opt/conda/envs/protean

tests/core/test_entity.py::TestEntity::test_find_by tests/core/test_entity.py::TestEntity::test_find_by_object_not_found_error tests/core/test_entity.py::TestEntity::test_find_by_multiple_attributes tests/core/test_entity.py::TestEntity::test_find_by_multiple_attributes_object_not_found_error tests/core/test_entity.py::TestEntity::test_save tests/core/test_entity.py::TestEntity::test_save_validation_error
tests/core/test_entity.py::TestEntity::test_init tests/core/test_entity.py::TestEntity::test_required_fields tests/core/test_entity.py::TestEntity::test_defaults tests/core/test_entity.py::TestEntity::test_validate_string_length tests/core/test_entity.py::TestEntity::test_validate_data_value_against_type tests/core/test_entity.py::TestEntity::test_template_init tests/core/test_entity.py::TestEntity::test_error_messages tests/core/test_entity.py::TestEntity::test_entity_inheritance tests/core/test_entity.py::TestEntity::test_default_id tests/core/test_entity.py::TestEntity::test_to_dict tests/core/test_entity.py::TestEntity::test_get tests/core/test_entity.py::TestEntity::test_get_object_not_found_error tests/core/test_entity.py::TestEntity::test_create_error tests/core/test_entity.py::TestEntity::test_create tests/core/test_entity.py::TestEntity::test_update_with_invalid_id tests/core/test_entity.py::TestEntity::test_update_with_dict tests/core/test_entity.py::TestEntity::test_update_with_kwargs tests/core/test_entity.py::TestEntity::test_update_with_dict_and_kwargs tests/core/test_entity.py::TestEntity::test_that_update_runs_validations tests/core/test_entity.py::TestEntity::test_unique tests/core/test_entity.py::TestEntity::test_filter tests/core/test_entity.py::TestEntity::test_pagination tests/core/test_entity.py::TestEntity::test_delete
src/protean/core/entity.py
src/protean/core/entity.py